Overview數組
自 WWDC 2015 推出和開源 Swift 2.0 後,你們對 Swift 的熱情又一次高漲起來,在羨慕創業公司的朋友們大談 Swift 新特性的同時,也有不少像我同樣工做上依然須要堅守着 Objective-C 語言的開發者們。今年的 WWDC 中介紹了幾個 Objective-C 語言的新特性,仍是在「與 Swift 協同工做」這種 Topic 裏講的,愈加凸顯這門語言的邊緣化了,不過有新特性仍是極好的,接下來,本文將介紹下面三個主要的新特性:測試
Nullability3d
Lightweight Generics *指針
__kindofcode
Nullability對象
然而 Nullability 並不算新特性了,從上一個版本的 llvm 6.1 (Xcode 6.3) 就已經支持。這個簡版的 Optional ,沒有 Swift 中 ? 和 ! 語法糖的支持,在 Objective-C 中就顯得很是囉嗦了:blog
假如用來修飾一個變量,前面還要加雙下劃線,放到 block 裏面就更加詭異,好比一個 Request 的 start 方法能夠寫成:繼承
除了這倆外,還有個 null_resettable 來表示 setter nullable,可是 getter nonnull,繞死了,最直觀例子就是 UIViewController 中的 view 屬性:接口
它能夠被設成 nil,可是調用 getter 時會觸發 -loadView 從而建立並返回一個非 nil 的 view。作用域
從 iOS9 SDK 中能夠發現,頭文件中全部 API 都已經增長了 Nullability 相關修飾符,想了解這個特性的用法,翻幾個系統頭文件就差不離了。接口中 nullable 的是少數,因此爲了防止寫一大堆 nonnull,Foundation 還提供了一對兒宏,包在裏面的對象默認加 nonnull 修飾符,只須要把 nullable 的指出來就行,黑話叫 Audited Regions:
Nullability 在編譯器層面提供了空值的類型檢查,在類型不符時給出 warning,方便開發者第一時間發現潛在問題。不過我想更大的意義在於可以更加清楚的描述接口,是主調者和被調者間的一個協議,比多少句文檔描述都來得清晰,打個比方:
NSURL 的這個 API 前面加了 nullable 後,更加顯式的指出了這個接口可能由於 URLString 的格式錯誤而建立失敗,使用時天然而然的就考慮到了判空處理。
不只是屬性和方法中的對象,對於局部的對象、甚至 c 指針均可以用帶雙下劃線的修飾符,能夠理解成能用 const 關鍵字的地方都能用 Nullability。
因此 Nullability 總的來講就是,寫着醜B,用着舒服 - -
Lightweight Generics
Lightweight Generics 輕量級泛型,輕量是由於這是個純編譯器的語法支持(llvm 7.0),和 Nullability 同樣,沒有藉助任何 objc runtime 的升級,也就是說,這個新語法在 Xcode 7 上可使用且徹底向下兼容(更低的 iOS 版本)
帶泛型的容器
這無疑是本次最重大的改進,有了泛型後終於能夠指定容器類中對象的類型了:
返回值的 id 被替換成具體的類型後,使人感動的代碼提示也出來了:
假如向泛型容器中加入錯誤的對象,編譯器會不開心的:
系統中經常使用的一系列容器類型都增長了泛型支持,甚至連 NSEnumerator 都支持了,這是很是 Nice 的改進。和 Nullability 同樣,我認爲最大的意義仍是豐富了接口描述信息,對比下面兩種寫法:
不用多想就清楚下面的數組中存的是什麼,避免了 NSString 和 NSURL 的混亂。
自定義泛型類
比起使用系統的泛型容器,更好玩的是自定義一個泛型類,目前這裏還沒什麼文檔,但攔不住咱們寫測試代碼,假設咱們要自定義一個 Stack 容器類:
這個 ObjectType 是傳入類型的 placeholder,它只能在 @interface 上定義(類聲明、類擴展、Category),若是你喜歡用 T 表示也 ok,這個類型在 @interface 和 @end 區間的做用域有效,能夠把它做爲入參、出參、甚至內部 NSArray 屬性的泛型類型,應該說一切都是符合預期的。咱們還能夠給 ObjectType 增長類型限制,好比:
若什麼都不加,表示接受任意類型 ( id );當類型不知足時編譯器將產生 error。
實例化一個 Stack,一切工做正常:
對於多參數的泛型,用逗號隔開,其餘都同樣,能夠參考 NSDictionary 的頭文件。
協變性和逆變性
當類支持泛型後,它們的 Type 發生了變化,好比下面三個對象看上去都是 Stack,但實際上屬於三個 Type:
當其中兩種類型作類型轉化時,編譯器須要知道哪些轉化是容許的,哪些是禁止的,好比,默認狀況下:
咱們能夠看到,不指定泛型類型的 Stack 能夠和任意泛型類型轉化,但指定了泛型類型後,兩個不一樣類型間是不能夠強轉的,假如你但願主動控制轉化關係,就須要使用泛型的協變性和逆變性修飾符了:
__covariant - 協變性,子類型能夠強轉到父類型(里氏替換原則)
__contravariant - 逆變性,父類型能夠強轉到子類型(WTF?)
協變:
效果:
逆變:
效果:
協變是很是好理解的,像 NSArray 的泛型就用了協變的修飾符,而逆變我尚未想到有什麼實際的使用場景。
__kindof
__kindof 這修飾符仍是很實用的,解決了一個長期以來的小痛點,拿原來的 UITableView 的這個方法來講:
使用時前面基本會使用 UITableViewCell 子類型的指針來接收返回值,因此這個 API 爲了讓開發者沒必要每次都蛋疼的寫顯式強轉,把返回值定義成了 id 類型,而這個 API 實際上的意思是返回一個 UITableViewCell 或 UITableViewCell 子類的實例,因而新的 __kindof 關鍵字解決了這個問題:
既明確代表了返回值,又讓使用者沒必要寫強轉。再舉個帶泛型的例子,UIView 的 subviews 屬性被修改爲了:
這樣,寫下面的代碼時就沒有任何警告了:
Where to go
有了上面介紹的這些新特性以及如 instancetype 這樣的歷史更新,Objective-C 這門古老語言的類型檢測和類型推斷終於有所長進,如今不管是接口仍是代碼中的 id 類型都愈來愈少,更多潛在的類型錯誤能夠被編譯器的靜態檢查發現。
同時,我的感受新版的 Xcode 對繼承鏈構造器的檢測也增強了,NS_DESIGNATED_INITIALIZER 這個宏並非新面孔,可使用它標誌出像 Swift 同樣的指定構造器和便捷構造器。
最後,附上一段用上了全部新特性的代碼,Swift 是發展趨勢,若是你暫時依然要寫 Objective-C 代碼,把全部新特性都用上,或許能讓你到新語言的遷移更無痛一點。