WWDC2015的明星是Swift。在Swift語言到2.0以后會被開源,這其中包括了protocol擴展和一個新的錯誤處理API。 蘋果的小baby已經長成,並且意料之中的獲得了開發者的關注。但是在iOS開發中Object-C並不會很快的推出歷史舞台。 並且在WWDC2015中介紹了ObjC的一個很好地特性。我們下面就來談一談ObjC的這個新特性:泛型。
我們先看一看下面的代碼:
let name: String
let surname: String
var friends: [Person]?
init(name: String, surname: String) {
self.name = name
self.surname = surname
}
}
非常簡單。這里定義了一個名為Person
的類。雖然更應該被定義為一個struct,但是為了和ObjC做對比就先定義為類了。 這個類里面定義了一個屬性friends
,一個Person對象組成的數組。Swift的數組是泛型的,可以包括Swift里面有的所有類型。 現在,假設我們有一個Person的對象,我們需要他的第一個朋友的名字。
編譯器知道person.friends
是一個optional的包含Person
對象的數組。所以firstFriendName
是一個可空的字符串
很好,那么在ObjC里是怎么樣的呢?
@property(nonatomic, copy, nonnull) NSString *name;
@property(nonatomic, copy, nonnull) NSString *surname;
@property(nonatomic, strong, nullable) NSArray *friends;
@end
在我們繼續之前,我們先來聊一下nonnull和nullable這兩個修飾符。這些叫做可空聲明,是在Xcode6.3中引入的。 __nullable
可以有nil
或者NULL
值,而__nonnull
不可以。如果你不遵守這些規則,那么是編譯不過的。
現在,我們可以回到泛型。我們無法在friends
數組中定義元素類型。參考Swift的例子,假設我們有類Person
的對象, 而且我們需要第一個朋友的名字。由於沒有泛型,我們首先需要取到朋友數組的第一個元素。
由於Objective-C里沒有泛型,person.friends.fristObject
只能定義為id
類型的,而不是Person
。 id
是一個可以指向任意類型的對象的指針,也就是id
指針指向的對象可以是任意類型的。 我們完全不能明確的知道person.friends.firstObject
是一個Person
對象。我們只能假設person.friends.firstObject
是一個Person
對象, 但是可以是NSString
類型的對象。
NSString *fristFriendWrongTypeVariable = person.friends.firstObject;
實用正確類型的對象是我們需要處理的。如果我們用了一個錯誤的類型,那么在運行時這個對象會接受到一個不支持的message, 這樣就會報錯了。要獲取第一個朋友的名字,我們需要初始化另外的一個變量:
NSString *firstFriendName = firstFriend.name
這個例子非常簡單,但是卻明顯的表明了ObjC需要額外多寫一些代碼,而且開發者,而不是編譯器,需要負責類型的安全。
如果說ObjC急需什么Swift或者Java、C#早就已經有的特性的話,那么就一定是泛型了。幸好,Xcode7帶來了一個輕量級的ObjC泛型。
@property(nonatomic, copy, nonnull) NSString *name;
@property(nonatomic, copy, nonnull) NSString *surname;
@property(nonatomic, strong, nullable) NSArray<Person *> *friends;
@end
現在我們可以定義集合里的元素類型了。
編譯器知道firstFriendName
是NSString類型的。如果我們給一個變量賦值一個錯誤的類型的對象會發生什么呢?
我們會收到一個warning!
Swift會如何引入這個ObjC的Person
類呢
var surname: String
var friends: [Person]?
輕量的泛型不止適用於NSArray
。還適用於其他兩個基礎集合類-NSDictionary
和NSSet
。
另外,我們也可以在我們自定義的類型中使用這些輕量級的泛型:
- (void)doSomethingWithGeneric: (T)object;
@end
@implementation MyCustomClass
- (void)doSomethingWithGeneric:(id)object {
}
@end
[myCostomObject doSomethingWithGeneric:@"hello, world"];
如果我們使用錯誤的類型呢?
Xcode會給出一個警告。
但是有些東西需要注意:ObjC的自定義泛型類和泛型的集合在引入Swift之后行為並不一樣。 NSArray
、NSSet
和NSDictionary
的類型在Swift中還是可用的。但是自定義的類的泛型參數在Swift中就不可用了。 所有的自定義類型又變回了AnyObject
。
Xcode7引入了輕量級泛型有什么好處呢?極大地減少了類型轉換的代碼。類型檢測的責任從開發者轉移到了編譯器。 代碼更加干凈,類型更加安全。但是,這並不是全部。在Xcode7前,Swift調用ObjC的framework要非常小心。 每一個ObjC的集合元素都需要從AnyObject
類型做轉換。引入了泛型之后就把ObjC和Swift的互操作的這個問題解決了。
from:https://netguru.co/blog/objective-c-generics