Swift 3 杂谈

swift

Swift 3 出来也有一阵子了,也对公司的项目做了升级,但是暂时还没有赶放上线,依旧用着2.3。相较于Swift 2.2 , Swift 3做了很大的改动,逐渐脱离OC的影子。
语法上很多对象去掉了NS开头,去掉了繁琐的命名。如 UIColor.redColor() 改为 UIColor.red , 变成了属性,还有方法的第一个参数如果不指定 _调用的时候也要写参数名等等...

本文主要讨论Swift 3中的一些坑和使用过程中的一些小技巧,排名无理由~~


@discardableResult 消除返回值警告

在Swift 3中,如果方法的返回值没有处理xCode会报一个警告,如果在方法前加上 @discardableResult 不处理的时候就不会有警告了。也可以用
_ = xxx() 来消除警告。


浮点数取余数和除法

在Swift 3中 ,如果你声明一个let m = 12.0 默认m是 Double , Double是不能和Float做运算的。CGFloat在32位设备上是Float32在64位设备上
Float64 , 所以如果一个Double和一个Float做运算时先要转换类型的

let m = 12.0
let n:CGFloat = 19.0

let x = CGFloat(m)/n
let k = m.multiplied(by: Double(n)) // 乘法
let y = m.divided(by: Double(n))    // 除法

但是取余算法是不能作用于浮点型的,如果这样就会报错CGFloat(m)%n
正确的做法是:

let z =  CGFloat(m).truncatingRemainder(dividingBy: n) //取余 12.0

AnyObjectAny

之前整个项目基本只用 AnyObject 代表大多数实例,基本也不和Any有什么交集。因为Swift 2 针对IntString 等结构体进行了转换,编译器会自动
桥接为NSNumberNSString这种对象类型 ,在swift3中AnyObject不能表示结构体了 。而 Any 可以代表 structclassfunc 等几乎所有类型。

这个改动导致项目很多地方都要随着改,而且大多数库也做了改变,如Alamofire的参数从[String:AnyObject]? 变成 [String:Any]?

值得一提的是Any不可以代表任何可空类型,不用指定Any?

栗子:

let str:String? = "xwwa"
var param:[String:Any] = ["x":1,"code":str]
// ["code": Optional("xwwa"), "x": 1]

str 是一个Optional类型的,输出出来也是Optional。因为我们以前的请求是需要在header中带参数的json机密,换成Any怎么都过不去,后来发现有Optional值。

这里写了个方法转化了下

func absArray(param:[String:Any])->[String:Any]{
    let res = param.map { (key,value) -> (String,Any?) in
        let newValue = Mirror(reflecting: value)
        if newValue.displayStyle == Mirror.DisplayStyle.optional{
            if let v = newValue.children.first?.value{
                return (key,v)
            }else{
                return (key,nil)
            }
        }
        return (key,value)
    }
    var newParam:[String:Any] = [:]
    res.forEach { (key,v) in
        newParam[key] = v
    }
    return newParam
}
print(absArray(param:param))    // ["code": "xwwa", "x": 1]

用了反射判断如果值是optional就取出他实际的值.


Swift 3中 Notification使用方法

extension Notification.Name {
    static let kNoticeDemo = Notification.Name("xx.xx.ww.ss")
}

class DE{
    func test(){
        NotificationCenter.default.post(name: Notification.Name.kNoticeDemo , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(demo), name: Notification.Name.kNoticeDemo, object: nil)
        
        NotificationCenter.default.removeObserver(self, name: Notification.Name.kNoticeDemo, object: nil)
    }
    @objc func demo(){
        
    }
}

自定义操作符

在swift 2中自定义操作符

infix operator =~ {
    associativity none
    precedence 130
}

现在在Swift 3中这样的话会报警告Operator should no longer be declared with body;use a precedence group instead

// 自定义操作符 别名类型
infix operator >>> : ATPrecedence
precedencegroup ATPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
}

直接指定操作符的类型,对这个类型进行定义,associativity: left 表示左结合
higherThan 优先级高于 AdditionPrecedence 这个是加法的类型
lowerThan 优先级低于 MultiplicationPrecedence 乘除

这里给出常用类型对应的group

  • infix operator || : LogicalDisjunctionPrecedence
  • infix operator && : LogicalConjunctionPrecedence
  • infix operator < : ComparisonPrecedence
  • infix operator <= : ComparisonPrecedence
  • infix operator > : ComparisonPrecedence
  • infix operator >= : ComparisonPrecedence
  • infix operator == : ComparisonPrecedence
  • infix operator != : ComparisonPrecedence
  • infix operator === : ComparisonPrecedence
  • infix operator !== : ComparisonPrecedence
  • infix operator ~= : ComparisonPrecedence
  • infix operator ?? : NilCoalescingPrecedence
  • infix operator + : AdditionPrecedence
  • infix operator - : AdditionPrecedence
  • infix operator &+ : AdditionPrecedence
  • infix operator &- : AdditionPrecedence
  • infix operator | : AdditionPrecedence
  • infix operator ^ : AdditionPrecedence
  • infix operator * : MultiplicationPrecedence
  • infix operator / : MultiplicationPrecedence
  • infix operator % : MultiplicationPrecedence
  • infix operator &* : MultiplicationPrecedence
  • infix operator & : MultiplicationPrecedence
  • infix operator << : BitwiseShiftPrecedence
  • infix operator >> : BitwiseShiftPrecedence
  • infix operator ..< : RangeFormationPrecedence
  • infix operator ... : RangeFormationPrecedence
  • infix operator *= : AssignmentPrecedence
  • infix operator /= : AssignmentPrecedence
  • infix operator %= : AssignmentPrecedence
  • infix operator += : AssignmentPrecedence
  • infix operator -= : AssignmentPrecedence
  • infix operator <<= : AssignmentPrecedence
  • infix operator >>= : AssignmentPrecedence
  • infix operator &= : AssignmentPrecedence
  • infix operator ^= : AssignmentPrecedence
  • infix operator |= : AssignmentPrecedence

合理的使用异常处理,提高代码质量

在日常开发中,可能遇到很多特殊情况,使得程序不能继续执行下去。有的来自系统语法方面,有的是来自业务方面的。这时候可以使用自定义异常,在底层代码中不断throw 在最后一层中去处理。

struct ZError : Error {
    let domain: String
    let code: Int
}

func canThrow() throws{
    let age = 10
    if a < 18{
        let error = ZError(domain: "xxx", code: 990)
        throw error
    }
    
}

do {
    try canThrow()
} catch let error as ZError {
    print("Error: \(error.code) - \(error.domain)") // Error: 990 - xxx
}

是时候放弃前缀的扩展了

以前我们要给UIView扩展是这样的

extension UIView {
    var zz_height:CGFloat{
        set(v){
            self.frame.size.height = v
        }
        get{
            return self.frame.size.height
        }
    }
}

这样在自己写的属性前面加一个前缀。但是Swift 3出来后更多的选择应该是这样的 view.zz.height 。 以前kingfisherimageView.kf_setImage
现在变成imageView.kf.setImageSnapKit 也改变成了 make.snp.left 之类的语法

那么怎么写这样的扩展呢?

这里看了KingFisher的代码,给出他的解决方案。比如我们想写一个UIView的扩展。


// 写一个协议  定义一个只读的类型
public protocol UIViewCompatible {
    associatedtype CompatableType
    var zz: CompatableType { get }
}


public extension UIViewCompatible {
    // 指定泛型类型为自身 , 自身是协议 谁实现了此协议就是谁了
    public var zz: Auto<Self> {
        get { return Auto(self) } // 初始化 传入自己
        set { }
    }
}

// Auto是一个接受一个泛型类型的结构体 
public struct Auto<Base> {
    // 定义该泛型类型属性 
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}

// 写一个Auto的扩展 指定泛型类型是UIView 或者其子类
public extension Auto where Base:UIView {
    
    var height:CGFloat{
        set(v){
            self.base.frame.size.height = v
        }
        get{
            return self.base.frame.size.height
        }
    }
}

// 扩展 UIView 实现  UIViewCompatible 协议,就拥有了zz属性 zz又是Auto类型  Auto是用泛型实例化的  这个泛型就是UIView了
extension UIView : UIViewCompatible{

}

// 使用

view.zz.height

上面的注释已经尽量详细的解释了这段代码,hope you can understand !


GCD 的改变

swift 3彻底改变了GCD的使用方式,这里给出日常最基本的几个
你不需要在去用 dispatch_get_main_queue ( ) 来获取主线程,而是 DispatchQueue . main ,那么要放到主线程的代码怎么执行呢?只需要在线程后边使用 . async { } 即可,也就是说,大概是这样:

DispatchQueue.main.async {
    print("这里在主线程执行")
}
优先级
  • DISPATCH_QUEUE_PRIORITY_HIGH: .userInitiated
  • DISPATCH_QUEUE_PRIORITY_DEFAULT: .default
  • DISPATCH_QUEUE_PRIORITY_LOW: .utility
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND: .background
//global 中设置优先级 不设置默认是 default
DispatchQueue.global(qos: .userInitiated).async {
    print("设置优先级")
}

创建一个队列

let queue = DispatchQueue(label: "im.demo.test")

也可以指定优先级和队列

let highQueue = DispatchQueue(label: "high.demo.test.queue", qos: DispatchQoS.background , attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit , target: nil )

highQueue.async {
    print("ceshi")
}

3秒后执行

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.0) {
    print("after")
}

根据View查找VC

如果你在一个UITableViewCell 或者 cell上自定义的一个view上想使用这个view所在的vc怎么办? 代理 ? 层层引用 ? NO ! 一个扩展解决。
一个UIView的扩展

// 查找vc
func responderViewController() -> UIViewController {
    var responder: UIResponder! = nil
    var next = self.superview
    while next != nil {
        responder = next?.next
        if (responder!.isKind(of: UIViewController.self)){
            return (responder as! UIViewController)
        }
        next = next?.superview
    }
    return (responder as! UIViewController)
}

记得写在扩展中哦,加上前面的技巧 。不论你在哪个view中。只需要这样let vc = view.zz.responderViewController() 就能拿到所处的vc了。


View中的第一响应者

又是一个View的扩展也很好用,上代码

func findFirstResponder()->UIView?{
    if self.isFirstResponder{
        return self
    }
    for subView in self.subviews{
        let view = subView.findFirstResponder()
        if view != nil {
            return view
        }
    }
    return nil
}

用法同上,这个东西能干啥呢?

利用这个可以在 NSNotification.Name.UIKeyboardWillShow通知通知中拿到第一响应者,如果第一响应者是UITextfield,可以算出局底下的距离,给挡墙view做变换。避免被覆盖。而且这个东西可以写在协议的默认实现中或者
写在一个基类中公用。本文为杂谈不细说了,点到为止咯!


暂时写这些吧,后面有时间再补。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容