改用swift來思考
現(xiàn)有代碼庫 + 你的頭腦 + Swift。 怎么會錯呢?
原文鏈接: Switching Your Brain to Swift
- 原文日期: 2015/08/17
- 譯文日期: 2015/09/04
- 譯者:ray16897188
本文原基于360iDev 2015的一次談話朋贬。等有視頻之后我會貼上連接碧磅。與此同時例获,何不看看我在elsewhere上其他的Swift文章杈抢,以及我Twitter的主頁呢?
完美的情形當然是從零開始的一個100% Swift的項目庆捺。如果你能這么做古今,很棒!但對與我們中的那些手里已經(jīng)有現(xiàn)成代碼庫滔以,而且有點兒想試試Swift的人來說捉腥,我們能從哪里開始?
為什么你画?
退一步:你為什么想用Swift去寫代碼呢抵碟?很多原因:Swift是新星;Swift有更好的語法(開始口水戰(zhàn)了)坏匪;Swift是Apple指引給我們的新方向拟逮。
將來,Swift的絕倫程度還會繼續(xù)增加剥槐,并會成為大眾首選的唱歧,有最完善的技術(shù)支持的宪摧,寫OSX和iOS代碼最簡便的一種語言粒竖。
![](http://gregheo.com/images/blog/objs-swift-awesomeness.png)
Swift是未來之路,就這么明了几于。那怎么做才能開始用Swift的方式去思考呢蕊苗?
Swift的方式
有太多要考慮的事情了,但我們從兩個大的方面說起:安全性和值語義(value semantics)沿彭。
安全性
Objective-C里的Nil棒極了朽砰。你可以給那個東西一直發(fā)消息, 好似明天是世界末日,運行時就會一直提供回應(yīng)瞧柔。
然而Swift中的Nil卻是另一個很不同的怪物漆弄。通常來講類型系統(tǒng)(type system)會把你從試圖調(diào)用空函數(shù)或者訪問空屬性中挽救出來,阻止你這么干造锅。但你能繞過類型系統(tǒng)撼唾,而這么做和在C語言中解引用一個空指針一樣糟糕:在運行時你會被友善的捕獲,然后你的app會崩潰哥蔚。
Swift中一切都關(guān)乎類型安全倒谷。一個String是一個String,那它就是一個String糙箍。此時根本就沒nil什么事兒渤愁。多想想C++中的引用而非C中的指針,因為引用永遠也不會是nil深夯。
可選類型
有了可選類型抖格,nil就又回來了。一個可選類型的String可以確實是一個String咕晋,或者是nil他挎。你必須做檢查,每一次都要捡需。
或者不做檢查:你可以強制將這個可選類型變量拆包办桨。或者將它換成一個隱式解析可選類型(implicitly unwrapped optional)站辉,這意味著它有些復(fù)雜的劣勢呢撞,但可以當非可選類型一樣去使用 - 直到它是nil的時候,app就崩潰了饰剥。
![(所有的都拆包!)](http://gregheo.com/images/blog/objs-unwrap.png)
Cocoa里有太多可選類型殊霞,這意味著你每用其中一個,就得去檢查里面是什么汰蓉。
這就是一個大的思維轉(zhuǎn)變绷蹲。其中的思路是:你不應(yīng)該留下任何偶然因素讓發(fā)送的消息為nil。你應(yīng)該知道顾孽,一個強類型系統(tǒng)中祝钢,某種東西要么是nil,要么就有值若厚。
如果在運行時有不確定性拦英,就做檢查。不要強制去拆包测秸。
把可選類型想成是一個盒子:這個盒子要么沒有值(nil)疤估,要么就有值灾常。但是你在強制拆包它之前總要先去檢查一下。
![(你得問:可選類型的盒子里面是啥?)](http://gregheo.com/images/blog/objs-box.jpg)
還有很多其他展示Swift安全性的例子:構(gòu)造器铃拇,更少量的未定義行為(less undefined behavior)钞瀑,內(nèi)存安全。Nil的安全性是總會出現(xiàn)的一種慷荔。
值類型
值類型在Swift中隨處可見仔戈。沒什么新東西 - Objective-C就有像NSInteger和類似CGRect的結(jié)構(gòu)體這樣的基元(primitives)。但其絕大多數(shù) - NSString拧廊,NSArray等等 - 都是類(classes)监徘,因此是引用類型。
Swift中則完全相反吧碾,如果你掃一眼頭文件的話會發(fā)現(xiàn)標準庫中有80多個結(jié)構(gòu)體凰盔,只有4個類。
String倦春,numbers和集合類型在Swift中都是值類型户敬。這意味著如果你有一個可變的Swift String(是結(jié)構(gòu)體)并把它傳給一個函數(shù),你獲得的是一個拷貝(copy)睁本。重復(fù)一遍尿庐,這并非一個嚇人的新概念:我們在Objective-C中已經(jīng)做了很久的copy和mutableCopy。這里大的轉(zhuǎn)變是對于多數(shù)常見類型來說這是新的缺省行為呢堰。
不幸的是抄瑟,如果你在Swift中用結(jié)構(gòu)體寫了些很棒的代碼,在Objective-C中你是不能回訪它們的枉疼。這就把我們帶到了下一個話題:橋接皮假。
橋接
Swift理所當然的被設(shè)計成了能與Objective-C良好協(xié)同工作。這基本上是一個必要的骂维,無需爭論的事實惹资,因為Cocoa是為Objective-C而建立。所有這些Cocoa的API必須能夠從Swift調(diào)用航闺,這意味著你自己的Objective-C的類也可以良好的橋接到Swift中褪测。
有個問題:從Swift開始,加入Objective-C代碼潦刃,然后想在Objective-C中調(diào)用你的Swift代碼侮措。
從Swift到Objective-C
有太多的Swift特性完全不能橋接到Objective-C里,比如Swift自己的結(jié)構(gòu)體和增強的枚舉類型福铅。這意味著如果你用Swift里所有最酷的特性寫出來的最新最強的framework萝毛,其中絕大多數(shù)并不能被Objective-C訪問到。
即使你給自己做限制滑黔,僅用Swift中那些可兼容Objective-C的特性去寫笆包,你也不能從Objective-C的類派生出一個用Swift寫的子類。你可以遵循table View或collection view的模式略荡,并用delegate和布局對象(layout objects)來規(guī)避這個問題庵佣,但是如果你的API是要被繼承從而派生子類的話,還是要時刻記住這一點汛兜。
Swift中任何東西在Objective-C中都默認不可見巴粪。
如果你把你的class和protocol標記了@objc,那它們就可以在Objective-C中用了粥谬。動態(tài)調(diào)節(jié)器(dynamic modifier)也暗含著@objc的標注肛根,讓被標記的東西在Objective-C中可用,但它使那些被標記了dynamic的屬性或者方法采用的是Objective-C的動態(tài)分發(fā)(dynamic dispatch)漏策。
![(Swift&Objective-C)](http://gregheo.com/images/blog/objs-objc-dynamic.png)
如果你想用swizzle之類的東西派哲,你就需要用dynamic;僅僅給它標識@objc還不足以保證objc_msgSend()能被使用掺喻,因為方法是有可能被去虛擬化(devirtualized)或者被內(nèi)聯(lián)了芭届。
重復(fù)一下,這僅僅對那些可兼容的特性有效感耙。你自己寫的Swift枚舉類型中的方法并不適用褂乍。如果你枚舉類型不是由Int型所構(gòu)建,那就兼容不了即硼。
Objective-C 到 Swift: 可空性(Nullability)
從Objective-C轉(zhuǎn)到Swift有很多好消息逃片。為了促進這個轉(zhuǎn)換過程,在Objective-C中你要給你的屬性只酥,參數(shù)和返回值類型加上標注(annotation)题诵。
- _Null_unspecified (default) – 橋接到一個Swift隱式解析可選類型(implicitly-unwrapped optional)。
- _Nonnull – 該值不可以是nil层皱;橋接到一個常規(guī)的引用性锭。
- _Nullable – 該值可以是nil;橋接到一個可選類型叫胖。
如果你給你的Objective-C加了標注草冈,你就會順利的將類型橋接到Swift中。即使你沒碰過Swift瓮增,你用Objective-C的時候這些標注也會在代碼補全后出現(xiàn)怎棱。如果你告訴編譯器一個方法的參數(shù)是_Nonnull的,然后傳進去一個nil绷跑,你就會得到一個合理的編譯器警告拳恋。
開始為代碼加標注是很好的做法。使用現(xiàn)有的API的時候標注會幫到你砸捏,你開始用Swift的時候也會讓你加快速度谬运。
Objective-C 到 Swift: 輕量泛型(Lightweight Generics)
輕量泛型是Swift 2的新特性隙赁。集合類型NSArray,NSDictionary(對值類型)和NSSet可以包含任何舊的NSObject類型梆暖。
這意味著大量的類型轉(zhuǎn)換(casting)伞访。Objective-C中沒有這個問題,但是記缀洳怠:Swift是關(guān)乎安全性的厚掷。正確的轉(zhuǎn)換涉及到大量的檢查。你不應(yīng)該強制的做類型轉(zhuǎn)換级解,而應(yīng)該先測試冒黑。
現(xiàn)在有了泛型,意味著你可以在Objective-C中這樣寫:
NSArray<NSString *> * _Nonnull
這是一個會包含NSString對象的NSArray勤哗。有了可空(nullability)的注釋抡爹,你能看見它還說了這個array不會為nil,你總會得到一個array俺陋。如果你了解Java或者C++就會熟悉此處的泛型語法豁延。
然后橋接到Swift就會是這樣:
[String]
一個非常清晰的Swift String數(shù)組。
小的補充說明:輕量泛型只能應(yīng)用于基本集合類:arrays腊状,dictionaries和sets诱咏。
在渾水上架橋
我會建議從零開始新建一個Swift的項目 - 100%完全的Swift。如果你有第三方的frameworks缴挖,它們是Swift寫的還是Objective-C寫的就不會有太多影響 - 你能調(diào)用其中的任何一種袋狞。
如果你有一個現(xiàn)有的代碼庫,而且你想要開始引入Swift:那就嘗試將橋接按照從Objective-C到Swift的方向進行映屋。比如view controllers和views的具體實例在Swift中就運行的很好苟鸯;它們繼承自NSObject,如果需要的話你是可以從Object-C中訪問到它們的棚点。
![(金門大橋)](http://gregheo.com/images/blog/objs-bridge.jpg)
但是對所有其他的來說:純Swift泛型早处,由非整型,嵌套類型瘫析,結(jié)構(gòu)體構(gòu)成的枚舉類型等等 - 就需要等到你身處于一個100% Swift世界中的那一天了砌梆。別被落下,這一天會比你想象的來的更早贬循。
在那天之前咸包,接著寫能夠友善兼容Swift的Objective-C的代碼并保持對Swift最新技術(shù)的認知吧。有太多的資源會幫助你完成過渡杖虾。
資源
- What's New in Swift 2 – 跟上Swift 2的最新特性
- Swift Guard Statement – 有人問過一個關(guān)于讓代碼保持"happy path(沒有異忱锰保或錯誤代碼,保證一切都按照所期望的情況運行 - 譯者注)"的問題奇适,Swift 2中的guard語句就能幫你實現(xiàn)這點坟比!
- Introducing Protocol-Oriented Programming in Swift 2 – 面向協(xié)議的編程(Protocol-oriented programming)是時下Swift的新熱點芦鳍。
- Using Swift with Cocoa and Objective-C (Swift 2 Prerelease) – Apple關(guān)于Swift + Objective-C + Cocoa的教程