系統(tǒng)庫范型
Objective C支持輕量級(jí)的范型。在編寫自定義的范型類之前,我們先來看看Cocoa Touch的集合類(NSArray,NSDictionary,NSSet)對(duì)于范型的支持。
首先創(chuàng)建一個(gè)數(shù)組罕扎,這個(gè)數(shù)組只應(yīng)該用來存儲(chǔ)字符串:
NSMutableArray * array = [[NSMutableArray alloc] init];
[array addObject:@"1"];
//誤加了一個(gè)非字符串類型進(jìn)去
[array addObject:@(0)];
這時(shí)候,對(duì)數(shù)組中元素進(jìn)行遍歷,Crash:
for (NSString *string in array) {
NSInteger length = [string length];
}
Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[__NSCFNumber length]: unrecognized selector sent to instance’
如果編譯器能幫助我們來確保加入數(shù)組中的元素都是String就好了
范型可以幫助我們解決這個(gè)問題:
//創(chuàng)建一個(gè)保存字符串的數(shù)組
NSMutableArray<NSString *> * array = [[NSMutableArray alloc] init];
[array addObject:@"1"];
[array addObject:@(0)];
同樣的昙篙,NSDictionary和NSSet也支持范型:
NSDictionary<NSString *, NSString *> *dictionary;
NSSet<NSString *> *set;
自定義范型類型
自定義一個(gè)范型類型很簡單,和其他語言類似诱咏,在聲明類的時(shí)候聲明占位符即可:
聲明一個(gè)Cup容器苔可,用來存儲(chǔ)一個(gè)值:
// Box.h
@interface Box<ObjectType> : NSObject
@property (nonatomic, readonly) ObjectType value;
- (void)seal:(ObjectType)value;
@end
// Box.m
@interface Box()
@property (strong, nonatomic) id value;
@end
@implementation Box
- (void)seal:(id)value{
_value = value;
}
@end
接著,我們就可以這樣使用袋狞,
Box<NSString *> * box = [[Box<NSString *> alloc] init];
[box seal:@"1234"];
[box seal:@(1)]; // Warning
關(guān)于Objective C范型的幾點(diǎn)說明:
- 在頭文件的類聲明中添加占位符ObjectType
- 在.m文件中無法使用范型占位符焚辅,用id類型替代即可
約束
Objective 可以為范型增加輕量級(jí)的約束,比如要求ObjectType實(shí)現(xiàn)NSCopying協(xié)議:
@interface Box<ObjectType:id<NSCopying>> : NSObject
那么苟鸯,只有實(shí)現(xiàn)NSCopying的類型才能夠通過編譯:
//Error
Box<NSObject *> * box1 = [[Box<NSObject *> alloc] init];
//Work
Box<NSString *> * box2 = [[Box<NSString *> alloc] init];
逆變與協(xié)變
創(chuàng)建一個(gè)ViewBox和LabelBox法焰,并且把LabelBox賦值給ViewBox
Box<UIView *> * viewBox = [[Box alloc] init];
Box<UILabel *> * labelBox = [[Box alloc] init];
viewBox = labelBox; // Warning
這看似合理,又不合理倔毙。
- 合理是因?yàn)閮蓚€(gè)Box中容納的類型不一樣埃仪,賦值的時(shí)候類型檢查不通過。
- 不合理是因?yàn)楦鶕?jù)里氏替換原則陕赃,一個(gè)容納UILabel的Box卵蛉,那么也應(yīng)該是一個(gè)容納UIView的Box颁股。
協(xié)變
協(xié)變由關(guān)鍵字__covariant聲明。一個(gè)協(xié)變類型的范型占位符傻丝,如果變量A的占位符類型是子類甘有,那么可以把它賦值給占位符類型是父類的B。
也就是說葡缰,通過協(xié)變亏掀,我們能夠解決:一個(gè)容納UILabel的Box,那么也應(yīng)該是一個(gè)容納UIView的Box泛释。
// Box.h
@interface Box<__covariant ObjectType> : NSObject
@property (nonatomic, readonly) ObjectType value;
- (void)seal:(ObjectType)value;
@end
協(xié)變常常用于容器類型滤愕,像系統(tǒng)的NSArray,NSDictionary怜校,NSSet都采用了協(xié)變:
逆變
逆變(__contravariant),一個(gè)逆變類型的范型占位符间影,如果變量A的占位符類型是父類,那么可以把它賦值給占位符類型是子類的B茄茁。
逆變更側(cè)重類型的行為.
舉個(gè)例子:
//用來解析字符串中的數(shù)字
@interface StringPaser<__contravariant ObjectType:NSString *> : NSObject
- (NSString *)paseNumber;
@end
那么以下代碼則不會(huì)有編譯器警告
StringPaser<NSString *> * stringPaser = [[StringPaser alloc] init];
StringPaser<NSMutableString *> * mutableStringPaser = [[StringPaser alloc] init];
//父類賦值給子類
mutableStringPaser = stringPaser;
逆變要求父類和子類能夠提供同樣的行為魂贬,所以通過父類的接口創(chuàng)建的范型類,可以用來處理子類裙顽。