swift3.0更新

2016-09-29 18:58
一:访问权限变更(原文链接:http://www.jianshu.com/p/604305a61e57)

新的访问控制fileprivate和open

在swift 3中新增加了两种访问控制权限 fileprivate和 open。下面将对这两种新增访问控制做详细介绍。

fileprivate

在原有的swift中的 private其实并不是真正的私有,如果一个变量定义为private,在同一个文件中的其他类依然是可以访问到的。这个场景在使用extension的时候很明显。

class User {
    private var name = "private"
}

extension User{
    var accessPrivate: String {
        return name
    }
}

这样带来了两个问题:

  • 当我们标记为private时,意为真的私有还是文件内可共享呢?
  • 当我们如果意图为真正的私有时,必须保证这个类或者结构体在一个单独的文件里。否则可能同文件里其他的代码访问到。
  • 由此,在swift 3中,新增加了一个 fileprivate来显式的表明,这个元素的访问权限为文件内私有。过去的private对应现在的fileprivate。现在的private则是真正的私有,离开了这个类或者结构体的作用域外面就无法访问。

    open

    open则是弥补public语义上的不足。
    现在的pubic有两层含义:

  • 这个元素可以在其他作用域被访问
  • 这个元素可以在其他作用域被继承或者override
  • 继承是一件危险的事情。尤其对于一个framework或者module的设计者而言。在自身的module内,类或者属性对于作者而言是清晰的,能否被继承或者override都是可控的。但是对于使用它的人,作者有时会希望传达出这个类或者属性不应该被继承或者修改。这个对应的就是 final。

    final的问题在于在标记之后,在任何地方都不能override。而对于lib的设计者而言,希望得到的是在module内可以被override,在被import到其他地方后其他用户使用的时候不能被override。

    这就是 open产生的初衷。通过open和public标记区别一个元素在其他module中是只能被访问还是可以被override。

    下面是例子:

    /// ModuleA:
    
    // 这个类在ModuleA的范围外是不能被继承的,只能被访问
    public class NonSubclassableParentClass {
    
        public func foo() {}
    
        // 这是错误的写法,因为class已经不能被继承,
        // 所以他的方法的访问权限不能大于类的访问权限
        open func bar() {}
    
        // final的含义保持不变
        public final func baz() {}
    }
    
    // 在ModuleA的范围外可以被继承
    open class SubclassableParentClass {
        // 这个属性在ModuleA的范围外不能被override
        public var size : Int
    
        // 这个方法在ModuleA的范围外不能被override
        public func foo() {}
    
        // 这个方法在任何地方都可以被override
        open func bar() {}
    
        ///final的含义保持不变
        public final func baz() {}
    }
    
    /// final的含义保持不变
    public final class FinalClass { }
    /// ModuleB:
    
    import ModuleA
    
    // 这个写法是错误的,编译会失败
    // 因为NonSubclassableParentClass类访问权限标记的是public,只能被访问不能被继承
    class SubclassA : NonSubclassableParentClass { }
    
    // 这样写法可以通过,因为SubclassableParentClass访问权限为 `open`.
    class SubclassB : SubclassableParentClass {
    
        // 这样写也会编译失败
        // 因为这个方法在SubclassableParentClass 中的权限为public,不是`open'.
        override func foo() { }
    
        // 这个方法因为在SubclassableParentClass中标记为open,所以可以这样写
        // 这里不需要再声明为open,因为这个类是internal的
        override func bar() { }
    }
    
    open class SubclassC : SubclassableParentClass {
        // 这种写法会编译失败,因为这个类已经标记为open
        // 这个方法override是一个open的方法,则也需要表明访问权限
        override func bar() { } 
    }
    
    open class SubclassD : SubclassableParentClass {
        // 正确的写法,方法也需要标记为open
        open override func bar() { }    
    }
    
    open class SubclassE : SubclassableParentClass {
        // 也可以显式的指出这个方法不能在被override
        public final override func bar() { }    
    }

    现在的访问权限则依次为:open,public,internal,fileprivate,private。
    有的人会觉得访问权限选择的增加加大了语言的复杂度。但是如果我们思考swift语言的设计目标之一就是一门安全的语言(“Designed for Safety”)就能理解这次的改动。更加明确清晰的访问权限控制可以使程序员表达出更准确的意图,当然也迫使在编码时思考的更加深入


    二:方法返回值

    Swift 3.0 中方法的返回值必须有接收否则会报警告,当然其实主要目的是为了避免开发人员忘记接收返回值的情况,但是有些情况下确实不需要使用返回值可以使用"_"接收来忽略返回值。当然你也可以增加@discardableResult声明,告诉编译器此方法可以不用接收返回值。

    struct Caculator {
        func sum(a:Int,b:Int) -> Int {
            return a + b
        }
        
        @discardableResult
        func func1(a:Int,b:Int) ->Int {
            return a - b + 1
        }
    }
    let ca = Caculator()
    ca.sum(a: 1, b: 2) // 此处会警告,因为方法有返回值但是没有接收
    let _ = ca.sum(a: 1, b: 2) // 使用"_"接收无用返回值
    ca.func1(a: 1, b: 2) // 由于func1添加了@discardableResult声明,即使不接收返回值也不会警告

    三:函数或方法参数

  • 调用函数或方法时从第一个参数开始就必须指定参数名
  • 在Swift的历史版本中出现过在调用函数时不需要指定任何函数参数(或者从第二个参数开始指定参数名),在调用方法时则必须从第二个参数开始必须指定参数名等多种情况,而在Swift3.0中不管是函数还是方法都必须从第一个参数开始必须指定参数名(当然可以使用“_”明确指出调用时省略参数)。

    // 从第一个参数就必须指定参数名,除非使用"_"明确指出省略参数
    func sum(num1:Int,num2:Int)->Int{
        return num1 + num2
    }
    
    sum(num1: 1, num2: 2) // old: sum(1,2)或者sum(1, num2: 2)
  • 取消var参数
  • //func increase(var a:Int){
    // a += 1
    //}
    // 上面的代码会报错,可改写成
    func increase(a:Int){
        var a = a
        a += 1
    }
  • inout参数修饰改放到类型前
  • //func increase(inout a:Int) {
    // a += 1
    //}
    // 上面的代码会报错,可改为
    func increase( a:inout Int) {
        a += 1
    }

    四:方法返回值

    Swift 3.0 中方法的返回值必须有接收否则会报警告,当然其实主要目的是为了避免开发人员忘记接收返回值的情况,但是有些情况下确实不需要使用返回值可以使用"_"接收来忽略返回值。当然你也可以增加@discardableResult声明,告诉编译器此方法可以不用接收返回值。

    struct Caculator {
        func sum(a:Int,b:Int) -> Int {
            return a + b
        }
        
        @discardableResult
        func func1(a:Int,b:Int) ->Int {
            return a - b + 1
        }
    }
    let ca = Caculator()
    ca.sum(a: 1, b: 2) // 此处会警告,因为方法有返回值但是没有接收
    let _ = ca.sum(a: 1, b: 2) // 使用"_"接收无用返回值
    ca.func1(a: 1, b: 2) // 由于func1添加了@discardableResult声明,即使不接收返回值也不会警告

    五:可选类型

    Swift3.0对于可选类型控制更加严谨,隐式可选类型和其他类型的运算之后获得的是可选类型而不是隐式可选类型。

    let a:Int! = 1
    let b = a + 1 // 此时强制解包,b是Int型
    let c = a // 注意此时c是Int? 在之前的Swift版本中c是Int!

    六:Selector的变化

    Selector的改变其实从1.0到3.0经历了多次变化,从最早的@Selector("method:")到现在的#selector(method(param1:))可以说经历了多次修改,好在它变得越来越好,毕竟字符串操作对于语法检查来说是很无助的。

    class MyClass {
        @objc func sum(a:Int,b:Int) -> Int {
            return a + b
        }
        
        func func1(){
            let _ = #selector(sum(a:b:))
        }
    }
    
    // old: Swift 2.2
    //class MyClass {
    // @objc func sum(a:Int,b:Int) -> Int {
    // return a + b
    // }
    // 
    // func func1(){
    // let _ = #selector(sum(_:b:))
    // }
    //}

    七:协议中的可选方法

    在Swift3.0之前如果要定义协议中可选方法,只需要给协议加上@objc之后方法使用optional修饰就可以了但是Swift3.0中除了协议需要@objc修饰可选方法也必须使用@objc来修饰

    @objc protocol MyProtocol {
        @objc optional func func1() //old: optional func func1()
        func func2()
    }

    八:取消++、--操作符

    var d = 1
    d++ //报错,可以改写成 d += 1 或者 d = d + 1

    九:取消C风格for循环

    //for var i = 0 ;i < 10 ; i += 1 {
    // debugPrint(i)
    //}
    // 上面的代码会报错,可改写成如下代码
    for i in 0  ..< 10  {
        debugPrint(i)
    }

    十:命名

    // 1.去掉前缀
    let url1 = URL(string: "www.cmjstudio.com")
    let isFileURL = url1?.isFileURL //old:url1.fileURL ,现在更加注重语意
    let data1 = Data() //NSData
    
    // 2.方法名使用动词,其他名词、介词等作为参数或移除
    var array1 = [1,2,3]
    array1.append(contentsOf: [4,5,6]) // old:array1.appendContentsOf([4,5,6])
    array1.remove(at: 0) // old:array1.removeAtIndex(0)
    
    // 3.不引起歧义的情况下尽量消除重复
    let color1 = UIColor.red() // old:var color1 = UIColor.redColor()
    
    // 4.枚举成员首字母变成小写
    let label1 = UILabel()
    label1.textAlignment = .center // old:label1.textAlignment = .Center
    
    // 5.按钮的Normal状态去掉
    let btn1 = UIButton()
    btn1.setTitle("hello", for: UIControlState()) // 相当于Normal状态

    十一:去C风格

    Swift发展初期很多类库的引入依然保持的ObjC风格,但是ObjC由于根出C语言,因此很多操作其实并不是对象和方法操作而是C语言的函数形式。到了Swift3.0之后这一现状将发生变化,全局函数将会变成某些类型的方法;某些常量定义将以某个枚举类型的成员来表示。

    let rect1 = CGRect(x: 0, y: 0, width: 100, height: 100)
    // 下面的代码将要报错,3.0完全废除这种类C的风格
    //let rect1 = CGRectMake(0, 0, 100, 100)
    
    if let context1 = UIGraphicsGetCurrentContext() {
        CGContext.fillPath(context1) // old:CGContextFillPath(context1!)
    }
    
    // GCD的改变
    let queue = DispatchQueue(label: "myqueue")
    queue.async {
        debugPrint("hello world!")
    }
    // old:
    //let queue = dispatch_queue_create("myqueue", nil)
    //dispatch_async(queue) {
    // debugPrint("hello world!")
    //}
    
    // 相关常量定义被移到枚举内部
    NotificationCenter.defaultCenter().addObserver(self, selector: #selector(userDefaultChange()), name: UserDefaults.didChangeNotification, object: nil)
    //old:NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(userDefaultChange()), name: NSUserDefaultsDidChangeNotification, object: nil)

    集合API的变化

    let array1 = [1,2,3]
    let next = array1.index(after: 0)  // old:let start = array1.startIndex let next = start.successor()
    let first = array1.first { (element) -> Bool in // 增加新的方法
        element > 1
    }
    
    let r = Range(0..<3) //old: let _ = NSRange(location: 0, length: 3)
    
    // 下面的代码必须在控制器中执行,用于遍历当前view及其父视图
    for subview in sequence(first: self.view, next: { $0?.superview }){
        debugPrint(subview)
    }
    十二:新的浮点协议

    Float、Double、CGFloat使用了新的协议,提供了提供 IEEE-754标准的属性和方法。

    let a = 2 * Float.pi // old: let a = 2 * M_PI
    let b = 2.0 * .pi // 注意前面是浮点型,后面可以省略Float
    
    
    十三:随机数函数的变化

    random()->arc4random()