之前 Apple 在 WWDC 上已將 Swift 3 整合進了 Xcode 8 beta 中,而本月蘋果發布了 Swift 3 的正式版。這也是自 2015 年底Apple開源Swift之后,首個發布的主要版本(Swift 3.0),該版本實現了 Swift 演變過程中所討論並通過的90多個提議。這里我對 Swift 3 的新特性、新變化進行一個總結。
一、徹底移除在 Swift 2.2 就已經棄用的特性
這些特性在我們使用 Xcode 7.3 的時候就已經有告警提示,在 Swift 3 中已將其徹底移出。
1、棄用 ++ 與 -- 操作符
過去我們可以使用 ++ 與 -- 操作符來實現自增自減,現已廢棄。
var i = 0
i++
++i
i--
--i
可以使用復合加法運算(+=)與減法運算(-=),或者使用普通的加法運算(+)與減法運算(-)實現同樣的功能。
2、廢除C語言風格的for循環
我們過去可能習慣下面風格的 for 循環,現在也已廢棄。
現在可以使用 for-in 循環,或者使用 for-each 加閉包的寫法實現同樣的功能。
//for-in循環
for i in 1...10 {
print(i)
}
//for-each循環
(1...10).forEach {
print($0)
}
3、移除函數參數的 var 標記
在 Swift 函數中,參數默認是常量。過去可以在參數前加關鍵字 var 將其定義為變量,這樣函數內部就可以對該參數進行修改(外部的參數任然不會被修改)。
var age = 22
add(age)
func add(var age:Int) {
age += 1
}
現在這種做法已經被廢棄,Swift 3 不再允許開發者這樣來將參數標記為變量了。
4、所有函數參數都必須帶上標簽
過去如果一個函數有多個參數,調用的時候第一個參數無需帶標簽,而從第二個參數開始,必須要帶標簽。
let number = additive(8, b: 12)
func additive(a:Int, b:Int) -> Int{
return a + b
}
現在為了確保函數參數標簽的一致性,所有參數都必須帶上標簽。
let number = additive(a: 8, b: 12)
func additive(a:Int, b:Int) -> Int{
return a + b
}
這個變化可能會造成我們的項目代碼要進行較大的改動,畢竟涉及的地方很多。所以蘋果又給出了一種不用給第一個參數帶標簽的解決方案。即在第一個參數前面加上一個下划線。
(不過這個只是方便我們代碼從 Swift2 遷移到 Swift3 的一個折中方案,可以的話還是建議將所有的參數都帶上標簽。)
let number = additive(8, b: 12)
func additive(_ a:Int, b:Int) -> Int{
return a + b
}
5、函數聲明和函數調用都需要括號來包括參數
我們可以使用函數類型作為參數 ,對於一個參數是函數、返回值也是函數的函數。原來我們可能會這么寫:
func g(a: Int -> Int) -> Int->Int { ... }
當這樣非常難以閱讀,很難看出參數在哪里結束,返回值又從哪里開始。在 Swift 3 中變成這么定義這個函數:
func g(a:(Int) -> Int) -> (Int) -> Int { ... }
6、Selector 不再允許使用 String
假設我們給按鈕添加一個點擊事件響應,點擊后執行 tapped 函數。以前可以這么寫:
button.addTarget(responder, action: "tapped", forControlEvents: .TouchUpInside)
但由於按鈕的 selector 寫的是字符串。如果字符串拼寫錯了,那程序會在運行時因找不到相關方法而崩潰。所以 Swift 3 將這種寫法廢除,改成 #selecor()。這樣就將允許編譯器提前檢查方法名的拼寫問題,而不用再等到運行時才發現問題。
button.addTarget(self, action:#selector(tapped), for:.touchUpInside)
二、Swift 3 的新特性
1、內聯序列函數sequence
Swift 3 新增了兩個全局函數:sequence(first: next:) 和 sequence(state: next:)。使用它們可以返回一個無限序列。下面是一個簡單的使用樣例,更詳細的介紹可以我的另一篇文章:Swift - 內聯序列函數sequence介紹(附樣例)
// 從某一個樹節點一直向上遍歷到根節點
for node in sequence(first: leaf, next: { $0.parent }) {
// node is leaf, then leaf.parent, then leaf.parent.parent, etc.
}// 遍歷出所有的2的n次方數(不考慮溢出)
for value in sequence(first: 1, next: { $0 * 2 }) {
// value is 1, then 2, then 4, then 8, etc.
}
2、 key-path不再只能使用String
這個是用在鍵值編碼(KVC)與鍵值觀察(KVO)上的,具體 KVC、KVO 相關內容可以參考我原來寫的這篇文章:Swift - 反射(Reflection)的介紹與使用樣例(附KVC介紹)
我們還是可以繼續使用 String 類型的 key-Path:
//用戶類
class User: NSObject{
var name:String = "" //姓名
var age:Int = 0 //年齡
}
//創建一個User實例對象
let user1 = User()
user1.name = "hangge"
user1.age = 100
//使用KVC取值
let name = user1.value(forKey: "name")
print(name)
//使用KVC賦值
user1.setValue("hangge.com", forKey: "name")
但建議使用新增的 #keyPath() 寫法,這樣可以避免我們因為拼寫錯誤而引發問題。
//使用KVC取值
let name = user1.value(forKeyPath: #keyPath(User.name))
print(name)
//使用KVC賦值
user1.setValue("hangge.com", forKeyPath: #keyPath(User.name))
3、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)
4、除了M_PI 還有 .pi
在過去,我們使用 M_PI 常量來表示 π。所以根據半徑求周長代碼如下:
let r = 3.0
let circumference = 2 * M_PI * r
在 Swift 3 中,π 提供了 Float,Double 與 CGFloat 三種形式(Float.pi、Double.pi、CGFloat.pi),所以求周長還可以這么寫:
let r = 3.0
let circumference = 2 * Double.pi * r
//我們還可以將前綴省略,讓其通過類型自動推斷
let r = 3.0
let circumference = 2 * .pi * r
5、簡化GCD的寫法
關於 GCD,我原來寫過一篇相關文章:Swift - 多線程實現方式(3) - Grand Central Dispatch(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")
}
6、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)
7、新增的訪問控制關鍵字:fileprivate、open
在 Swift 3 中在原有的 3 個訪問控制關鍵字 private、public、internal 外。又添加了2個新關鍵字 fileprivate、open。它們可以看成是對原來 private 和 public 的進一步細分。具體使用方法和介紹可以看我寫的另一篇文章:Swift - Swift3新增的兩個訪問控制關鍵字介紹(fileprivate、open)
三、一些語法的修改
1、數組排序:sort()與sorted()
過去數組排序的兩個方法:sortInPlace() 和 sort(),現在分別改名成 sort() 和 sorted()
sort() 是直接對目標數組進行排序。sorted() 是返回一個排序后的數組,原數組不變。
var array1 = [1, 5, 3, 2, 4]
array1.sort()
print(array1) //[1, 2, 3, 4, 5]
var array2 = [1, 5, 3, 2, 4]
let sortedArray = array2.sorted()
print(array2) //[1, 5, 3, 2, 4]
print(sortedArray) //[1, 2, 3, 4, 5]
2、reversed()與enumerated()
過去 reverse() 方法實現數組反轉,enumerate() 方法實現遍歷。現這兩個方法都加上 ed 后綴(reversed、enumerated)
for i in (1...10).reversed() {
print(i)
}
let array = [1, 5, 3, 2, 4]
for (index, value) in array.enumerated() {
print("\(index + 1) \(value)")
}
3、CGRect、CGPoint、CGSize
過去的 CGRectMake、CGPointMake、CGSizeMake 已廢棄。現改用 CGRect、CGPoint、CGSize 代替。
//Swift 2
let frame = CGRectMake(0, 0, 20, 20)
let point = CGPointMake(0, 0)
let size = CGSizeMake(20, 20)
//Swift 3
let frame = CGRect(x: 0, y: 0, width: 20, height: 20)
let point = CGPoint(x: 0, y: 0)
let size = CGSize(width: 20, height: 20)
4、移除了API中多余的單詞
- XCPlaygroundPage.currentPage 改為 PlaygroundPage.current
- button.setTitle(forState) 改為 button.setTitle(for)
- button.addTarget(action, forControlEvents) 改為 button.addTarget(action, for)
- arr.minElement() 改為 arr.min()
- arr.maxElement() 改為 arr.max()
- attributedString.appendAttributedString(anotherString) 改為 attributedString.append(anotherString)
- names.insert("Jane", atIndex: 0) 改為 names.insert("Jane", at: 0)
- NSBundle.mainBundle() 改為 Bundle.main
- UIDevice.currentDevice() 改為 UIDevice.current
- NSData(contentsOfURL) 改為 Data(contentsOf)
- NSJSONSerialization.JSONObjectWithData() 改為 JSONSerialization.jsonObject(with)
- UIColor.blueColor() 改為 UIColor.blue
5、枚舉成員變成小寫字母開頭
Swift 3 將枚舉成員當做屬性來看,所以現在使用小寫字母開頭而不是以前的大寫字母。
.system //過去是:.System
.touchUpInside //過去是:.TouchUpInside
.fillStroke //過去是:.FillStroke
.cgColor //過去是:.CGColor
6、@discardableResult
在 Swift 3 中,如果一個方法有返回值。而調用的時候沒有接收該方法的返回值,Xcode 會報出警告,告訴你這可能會存在潛在問題。
![2016092716530979317[2] 2016092716530979317[2]](/image/aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvMzM3NDA5LzIwMTYxMi8zMzc0MDktMjAxNjEyMTMxMTI2MTgzMzktODg2MjQxMzIucG5n.png)
除了可以通過接收返回值消除警告。還可以通過給方法聲明 @discardableResult 來達到消除目的。
.system //過去是:.System
.touchUpInside //過去是:.TouchUpInside
.fillStroke //過去是:.FillStroke
.cgColor //過去是:.CGColor
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
printMessage(message: "Hello Swift 3!")
}
@discardableResult
func printMessage(message: String) -> String {
let outputMessage = "Output : \(message)"
return outputMessage
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
原文出自:航歌網