引言
Swift,蘋果于2014年WWDC(蘋果開發(fā)者大會)發(fā)布的新開發(fā)語言,可與Objective-C共同運(yùn)行于Mac OS和iOS平臺,用于搭建基于蘋果平臺的應(yīng)用程序。Swift吸收了眾多現(xiàn)代編程語言的優(yōu)點,盡力的提供簡潔的編程語言和強(qiáng)大的功能。
WWDC 2017 給大家?guī)砹撕芏囿@喜。Swift 4 也伴隨著 Xcode 9 測試版來到了我們的面前,很多強(qiáng)大的新特性非常值得我們期待在正式項目中去使用它。因為 Swift 4 是開源的,如果你關(guān)注 swift-evolution 這個項目的話,就應(yīng)該已經(jīng)提前了解到它的新特性了。本文參考了 WWDC 2017 以及各種資料,,從語法、字符串、標(biāo)準(zhǔn)庫、構(gòu)建過程等方面,把 Swift 4 的這些新特性一一列舉出來做介紹和分析,讓他們毫無保留地展現(xiàn)在你眼前,下面話不多說了,來隨著小編一起看看詳細(xì)的介紹吧。
一、語法改進(jìn)
extension 中可以訪問 private 的屬性
考慮以下代碼:
struct Date: Equatable, Comparable { private let secondsSinceReferenceDate: Double static func ==(lhs: Date, rhs: Date) -> Bool { return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate } static func <(lhs: Date, rhs: Date) -> Bool { return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate }}
上面代碼定義了一個 Date 結(jié)構(gòu)體,并實現(xiàn) Equatable 和 Comparable 協(xié)議。為了讓代碼更清晰,可讀性更好,一般會把對協(xié)議的實現(xiàn)放在單獨的 extension 中,這也是一種非常符合 Swift 風(fēng)格的寫法,如下:
struct Date { private let secondsSinceReferenceDate: Double}extension Date: Equatable { static func ==(lhs: Date, rhs: Date) -> Bool { return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate }}extension Date: Comparable { static func <(lhs: Date, rhs: Date) -> Bool { return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate }}
但是在 Swift 3 中,編譯就報錯了,因為 extension 中無法獲取到 secondsSinceReferenceDate 屬性,因為它是 private 的。于是在 Swift 3 中,必須把 private 改為 fileprivate。
struct Date { fileprivate let secondsSinceReferenceDate: Double}...
但是如果用 fileprivate,屬性的作用域就會比我們需要的更大,可能會不小心造成屬性的濫用。
在 Swift 4 中,private 的屬性的作用域擴(kuò)大到了 extension 中,并且被限定在了 struct 和 extension 內(nèi)部,這樣就不需要再改成 fileprivate 了,這是最好的結(jié)果。
類型和協(xié)議的組合類型
考慮以下代碼:
protocol Shakeable { func shake()}extension UIButton: Shakeable { /* ... */ }extension UISlider: Shakeable { /* ... */ }func shakeEm(controls: [???]) { for control in controls where control.state.isEnabled { } control.shake()}
在 Swift 3 中,這里的 ??? 應(yīng)該寫什么呢?如果寫 UIControl,那么 control.shake() 就會報錯;如果寫 Shakeable,那么 control.state.isEnabled 就會報錯。其實我們也可以這樣寫:
func shakeEm(controls: [UIControl]) { for control in controls where control.isEnabled { if control is Shakeable { (control as! Shakeable).shake() } }}
這樣寫雖然可以跑通了,但是很丑陋。
在 Swift 4 中,可以把類型和協(xié)議用 & 組合在一起作為一個類型使用,就可以像下面這樣寫了:
protocol Shakeable { func shake()}extension UIButton: Shakeable { /* ... */ }extension UISlider: Shakeable { /* ... */ }func shakeEm(controls: [UIControl & Shakeable]) { for control in controls where control.state.isEnabled { control.shake() }// Objective-C API@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem@property (nullable, weak) NSView <NSTextInputClient> *client;@end}
把它聲明為了 UIControl & Shakeable 類型。OK,圓滿解決。
PS:
這個代碼例子是 WWDC 2017 的 PPT 中的,上面的代碼有點問題,control.state.isEnabled 這句代碼中,state 是沒有 isEnabled 這個屬性的,改為 control.isEnabled 就可以了。看來蘋果的工程師做 PPT 有時候還是不太嚴(yán)謹(jǐn)。
另外,iOS SDK 中的 API 也用這個特性做了優(yōu)化,例如:
// Objective-C API@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem@property (nullable, weak) NSView <NSTextInputClient> *client;@end
這個 API 的 Objective-C 版本是沒有問題的,可以知道 client 屬性既是一個 NSView,又符合 NSTextInputClient 協(xié)議。然而它對應(yīng)的 Swift 3 版本為:
class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem { var client: NSView?}
僅僅是一個 NSView 類型 /(ㄒoㄒ)/~~
在 Swift 4 中,這類 API 做了優(yōu)化,改成了:
class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem { var client: (NSView & NSTextInputClient)?}
這樣類型的聲明就更加嚴(yán)謹(jǐn)了。
Associated Type 可以追加 Where 約束語句
在 Swift 4 中可以在 associatedtype 后面聲明的類型后追加 where 語句
associatedtype Element where <xxx>
看下面是 Swift 4 標(biāo)準(zhǔn)庫中 Sequence 中 Element 的聲明:
protocol Sequence { associatedtype Element where Self.Element == Self.Iterator.Element // ...}
它限定了 Sequence 中 Element 這個類型必須和 Iterator.Element 的類型一致。
通過 where 語句可以對類型添加更多的約束,使其更嚴(yán)謹(jǐn),避免在使用這個類型時做多余的類型判斷。
新的 Key Paths 語法
先來看看 Swift 3 中 Key Paths 的寫法:
@objcMembers class Kid: NSObject { dynamic var nickname: String = "" dynamic var age: Double = 0.0 dynamic var friends: [Kid] = []}var ben = Kid(nickname: "Benji", age: 5.5)let kidsNameKeyPath = #keyPath(Kid.nickname)let name = ben.valueForKeyPath(kidsNameKeyPath)ben.setValue("Ben", forKeyPath: kidsNameKeyPath)
Swift 4 中創(chuàng)建一個 KeyPath 用 `` 作為開頭:
/Kid.nickname
當(dāng)編譯器可以推導(dǎo)出類型時,可以省略基礎(chǔ)類型部分:
/.nickname
上面的代碼在 Swift 4 中就可以這樣寫:
struct Kid { var nickname: String = "" var age: Double = 0.0 var friends: [Kid] = []}var ben = Kid(nickname: "Benji", age: 8, friends: [])let name = ben[keyPath: /Kid.nickname]ben[keyPath: /Kid.nickname] = "BigBen"
相比 Swift 3,Swift 4 的 Key Paths 具有以下優(yōu)勢:
下標(biāo)支持泛型
有時候會寫一些數(shù)據(jù)容器,Swift 支持通過下標(biāo)來讀寫容器中的數(shù)據(jù),但是如果容器類中的數(shù)據(jù)類型定義為泛型,以前的下標(biāo)語法就只能返回 Any,在取出值后需要用 as? 來轉(zhuǎn)換類型。Swift 4 定義下標(biāo)也可以使用泛型了。
struct GenericDictionary<Key: Hashable, Value> { private var data: [Key: Value] init(data: [Key: Value]) { self.data = data } subscript<T>(key: Key) -> T? { return data[key] as? T }}let dictionary = GenericDictionary(data: ["Name": "Xiaoming"])let name: String? = dictionary["Name"] // 不需要再寫 as? String
二、字符串
Unicode 字符串在計算 count 時的正確性改善
在 Unicode 中,有些字符是由幾個其它字符組成的,比如 é 這個字符,它可以用 /u{E9} 來表示,也可以用 e 字符和上面一撇字符組合在一起表示 /u{65}/u{301}。
考慮以下代碼:
var family = "
注:相關(guān)教程知識閱讀請移步到swift教程頻道。
新聞熱點
疑難解答
圖片精選