Swift 3 杂谈

作者: smalldu | 来源:发表于2016-10-20 20:53 被阅读2017次
    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做变换。避免被覆盖。而且这个东西可以写在协议的默认实现中或者
    写在一个基类中公用。本文为杂谈不细说了,点到为止咯!


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

    相关文章

      网友评论

      • LJ的ios开发:根据View查找VC 你写错了(var next = self.superview)应该改成(var next = self)
      • 07d93406ec39:最後兩個有點意思

      本文标题:Swift 3 杂谈

      本文链接:https://www.haomeiwen.com/subject/dmqquttx.html