To Swift 3.0

2016 - 09 - 14

编译器和语法变化

函数或方法参数

  • 调用函数或方法时从第一个参数开始就必须指定参数名

在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)  
}  

SDK类库变化

大家都知道Swift诞生在Objective-C已经发展的相当成熟的情况下,为了保证ObjC开发人员顺利过渡到Swift,也因为Swift处于初级阶段,很多类库和方法命名都尽量和ObjC保持一致,在使用Swift开发iOS应用中处处可以看到ObjC的影子。但是作为一门Modern语言Swift还是做出了改变,从中可以看出日后Swift将彻底摆脱ObjC的影子。这其中包括重新导入Foundation消除类型前缀、方法名去重、函数和方法去C风格等等。

命名

// 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)  
}  

Foundation 去掉 NS 前缀

比如过去我们使用 Foundation 相关类来对文件中的 JSON 数据进行解析,这么写:

let file = NSBundle.mainBundle().pathForResource("tutorials", ofType: "json")  
let url = NSURL(fileURLWithPath: file!)  
let data = NSData(contentsOfURL: url)  
let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: [])  
print(json)  

在 Swift 3 中,将移除 NS 前缀,就变成了:

let file = Bundle.main.path(forResource: "tutorials", ofType: "json")  
let url = URL(fileURLWithPath: file!)  
let data = try! Data(contentsOf: url)  
let json = try! JSONSerialization.jsonObject(with: data)  
print(json)  

简化GCD的写法

过去写法采用 C 语言的风格,初学者可能会不大适应。比如创建一个简单的异步线程:

let queue = dispatch_queue_create("Swift 2.2", nil)  
dispatch_async(queue) {  
    print("Swift 2.2 queue")  
}  

Swift 3 取消了这种冗余的写法,而采用了更为面向对象的方式:

   let queue = DispatchQueue(label: "Swift 3")  
queue.async {  
    print("Swift 3 queue")  
}  

Core Graphics的写法也更加面向对象化

Core Graphics 是一个相当强大的绘图框架,但是和 GCD 一样,它原来的 API 也是 C 语言风格的。
比如我们要创建一个 view,其内部背景使用 Core Graphics 进行绘制(红色边框,蓝色背景)。过去我们这么写:

class View: UIView {  
    override func drawRect(rect: CGRect) {  
        let context = UIGraphicsGetCurrentContext()  
        let blue = UIColor.blueColor().CGColor  
        CGContextSetFillColorWithColor(context, blue)  
        let red = UIColor.redColor().CGColor  
        CGContextSetStrokeColorWithColor(context, red)  
        CGContextSetLineWidth(context, 10)  
        CGContextAddRect(context, frame)  
        CGContextDrawPath(context, .FillStroke)  
    }  
}  
let frame = CGRect(x: 0, y: 0, width: 100, height: 50)  
let aView = View(frame: frame)  

在 Swift 3 中改进了写法,只要对当前画布上下文解包,之后的所有绘制操作就都基于解包对象。

class View: UIView {  
    override func draw(_ rect: CGRect) {  
        guard let context = UIGraphicsGetCurrentContext() else {  
            return  
        }  
        let blue = UIColor.blue.cgColor  
        context.setFillColor(blue)  
        let red = UIColor.red.cgColor  
        context.setStrokeColor(red)  
        context.setLineWidth(10)  
        context.addRect(frame)  
        context.drawPath(using: .fillStroke)  
    }  
}  
let frame = CGRect(x: 0, y: 0, width: 100, height: 50)  
let aView = View(frame: frame)  

新增的访问控制关键字:fileprivate、open

在 Swift 3 中在原有的 3 个访问控制关键字 private、public、internal 外。又添加了2个新关键字 fileprivate、open。它们可以看成是对原来 private 和 public 的进一步细分。

Other

@discardableResult
枚举成员变成小写字母开头
移除了API中多余的单词
动词与名词
#keyPath()
除了M_PI 还有 .pi

当然Swift3.0中还有一些其他的变化,如果感兴趣可以访问Swift Evolution

Table of Contents