剛開始做iOS開發的那些日子。 我仍然記得幾年前買的那些老書籍。現在他們靜靜的躺在我辦公室的書架上。多年前我虔誠的翻閱它們的時候,我不禁擔心Objective-C做不到什麼而不是它能做到的事。 今年(Objective-C)能做更多事了。本週我們會看到一個成熟的老朋友。讓我們來看下Objective-C有什麼提高吧。
設定
朋友們,讓我們去那條走過很多次的小道散散步。
1 |
@property (strong, nonatomic) NSArray *someViews; |
這是一個熟練的Objective-C開發者的標準寫法。表示包含一些檢視的集合屬性。但是有一些內在的缺陷隱藏在這一小段程式碼中。 它可以返回空嗎? 除非有文件說明或者這個開發者在現場,我們不能單憑看這些程式碼得到答案。 這裡面除了UIView型別物件之外還有其他型別的物件嗎? 和上面類似,誰知道呢?或許可以使用反射做判斷(譯者注:這裡指的是isKindOfClass之類的方法)?或者這裡能有除了UIView之外的物件嗎?或者說UIView的任何子類? 看起來這會需要很多轉換 因為這是包含某些物件的陣列,當某人找出這個陣列裡面物件型別——用它來做一些事時會花費很長的時間。 這削弱了Swift程式碼和可讀性 不幸的是,Swift有泛型。這意味著,一個集合型別被看做可選的任意物件型別。當使用這個屬性的時候,(這個特點)強迫開發者對Swift和Objective-C進行轉換。
可空註釋
當你知道一個簡單的屬性會引起這麼多問題時可能有點不舒服。維護有潛在問題的程式碼更容易出錯,更別提在上述語言和一個新的語言(如Swift)之間互動了。 能夠讓這件事變的簡單的第一步是我最喜歡的Objective-C新特性——可空註釋。這些註釋在程式設計中提供了一些非常有價值的東西。 意圖 他們詳細地描述了一個API。能返回空(nil),不能返回空。簡單來說,可以避免花幾個小時在除錯上。有三個可用值:
- nullable(可以為 nil 或 NULL)——對應(在Swift中的使用方式)’ UIView?’
- nonnull (不為 nil,如果傳 nil 給該指標,將會收到編輯器的警告)——對應(在Swift中的使用方式)’UIView’
- null_unspecified(是否為 nil 是不確定的)——對應(在Swift中的使用方式)’UIView!’
讓我們再看一下例子中的那個屬性。假設這個屬性是用來在執行時建立一些使用者介面的迭代器。這種情況下,這裡應該存放的是一些按鈕和檢視。 但是天吶——他們在任何情況下都不應該為空。現在應該這樣寫:
1 |
@property (strong, nonatomic, nonnull) NSArray *someViews; |
不僅這對Objective-C有幫助,現在這個屬性也使得Swift裡不再到處是optionals。 開發者看到這些程式碼就知道它是不是會返回空指標。好極了。 我們已經改良了靜態檢查、Swift的可用性以及這些API使用時最重要的意圖。
泛型
全世界使用Objective-C的開發者都很高興。哎呀,泛型遍佈各地,這些應該歸功於勇敢的開發者。 如果Cocoa Touch是孩子的睡前故事並且Objective-C是主人公,這本書肯定以上面幾句話結尾。長期以來,泛型的缺席一直是很多開發者的痛處。 儘管用了32年,Objective-C現在有泛型了。儘管沉浸在Swift 2 釋出的喜悅中,WWDC 15的參會者和消費者等不應該輕視這個訊息。它代表了很多改變,而且大多都是積極的。 回到我們的屬性。我們現在可以寫下來告訴編譯器和開發者這裡要求的是UIView。
1 |
@property (strong, nonatomic, nonnull) NSArray<UIView *> *someViews; |
這很好,因為如果一個人試著把除了UIViews之外的物件賦值給這個屬性,那麼編譯器會提示和警告。這也會省去了一些頭疼的轉換。 Swift也很高興。在最近的更新中,我們讓Swift知道一些物件不是可選的。現在Swift也知道這裡麵包含了UIView,所以可以擺脫任何有歧義的物件宣告。 開發者可以像C#、C++、Swtft以及其他語言一樣,使用<>括號標識型別。雖然這(符號)代表了遵循協議一致性,編譯器經過推斷知道何時、何地、如何運用它們。 長遠來看,引數化擴充套件(extensions)、類目(categories)和類(classes)也會成為可能。帶來的好處不僅在集合類。泛型這個特性可以用在Objective-C的任何地方,容器類只是其中之一。例如,看著下面的NSDictionary 類會讓你展開微笑:
1 2 3 |
@interface NSDictionary<KeyType, ObjectType> (Lookup) - (nullable ObjectType)objectForKey:(KeyType)aKey; @end |
儘管在我知道泛型是通過型別擦除來實現時有點失望,考慮到Objective-C老程式碼中大量遺留問題你就會懂(為什麼這樣做)。 型別擦除實現了二進位制相容,它也允許不改變Objective-C執行時。所以親愛的開發者,皺皺眉頭,抱怨一下C#的泛型實現確實比任何其他語言都好,我們還是要接著工作。
KindOf 型別
哎,我們的旅行還剩重要的一步。回憶一下我們提到可以包UIView的屬性。所以,正常情況下能推斷這裡麵包含了一些檢視和按鈕。 有了這些,當有人寫出下面這樣的程式碼時會發生什麼呢:
1 |
[self.someViews[0] addTarget:self action:selector(aMethod:) forControlEvents:UIControlEventTouchUpInside]; |
啊,一個編譯器警告。 這很正常,因為儘管在這個屬性中插入一個按鈕是有效的,並且你也可以說它是一個檢視,在轉換前無法確定它就是一個按鈕。 這種情況比想象中發生的要多,利用KindOf新特性很容易解決這個問題。回到這個例子中的屬性。
1 |
@property (strong, nonatomic, nonnull) NSArray<__kindof UIView *> *someViews; |
實際上,我們告訴編譯器這個屬性和它的容器可以包含UIView型別的物件。型別契約中支援了更多的我們以前沒有的型別資訊。 其本質是什麼?向下轉型。 這意味著程式碼在編譯器上執行良好,因為編譯器知道肯定有一個按鈕在容器內。 現在,如果你再回頭看最初的擔心,他們都被搞定了。 當Swift否定者對(Objective-C)的價值大加讚賞時,他們也應該心存感激。因為(Objective-C語言)價值的提升基於一點:Swift的互動性。 儘管如此,在一些特殊的場合,Objective-C是比以前更強大。
總結
Objective-C是我程式設計生涯的初戀。它是與眾不同(到現在仍是),它的顯著優點以及缺點都讓我著迷。現在Swift快速贏得了我這顆程式設計之心,我仍然發現我在使用Objective-C時很快樂。這些新特性很受歡迎。它們能夠被用來編寫更好的程式碼,並且它們已經在Foundation框架中隨處可見了。