為什么要使用泛型
在使用NSArray, NSSet, NSDictionary 中都有使用泛型.
先來看一段沒有使用泛型的的代碼
NSMutableArray *arr = [NSMutableArray array];
[arr addObject:@1];
[arr addObject:@"wang"];
[arr addObject:@{}];
在這段代碼中可以給數(shù)組中添加任意對象, 但是實(shí)際情況中我們希望使用數(shù)組用來保存同一種類型的變量.
實(shí)際情況中,我們可能希望使用數(shù)組保存一組NSString,但是我們操作失誤在數(shù)組中添加的一個(gè)NSNumber使用上面的代碼,在編譯階段,Xcode并不會(huì)提示錯(cuò)誤或給出警告,但是在實(shí)際使用中,我們認(rèn)為這個(gè)數(shù)組中都是NSString類型的變量,導(dǎo)致對NSNumber使用NSString的方法導(dǎo)致程序crash,顯然這樣的程序是不健壯的.
還好使用泛型可以解決這樣的問題,代碼如下
使用泛型指定數(shù)組中的類型為NSString,當(dāng)我們在數(shù)組中添加其他類型時(shí),就會(huì)出現(xiàn)提示.
在我們沒有指定泛型時(shí),提示顯示可以添加id類型
當(dāng)我指定泛型時(shí),提示的是在聲明過程中與泛型對應(yīng)的類型.
使用集合時(shí)用泛型聲明可以幫助你檢查集合中的類型,還能提示集合中應(yīng)存放的類型.
如何使用泛型
我們先看一下NSArray中泛型的使用
通過觀察發(fā)現(xiàn),泛型起到的作用就是占位符的作用.
聲明一個(gè)數(shù)組的指定泛型為NSString *, ObjectType就是一個(gè)占位符, 在接口中任何使用ObjectType泛型占位符的時(shí)候都會(huì)替換為NSString *
類型.
了解了系統(tǒng)泛型使用方法然后自己創(chuàng)建一個(gè)ClassStack
使用泛型.
@interface Stack <__covariant T> : NSObject
- (void)push:(T)obj;
- (T)pop;
@end
在聲明.h 我們都可以使用泛型T
作為占位符.
在類擴(kuò)展和分類中并不能使用T
泛型.
如果要做分類和類擴(kuò)展中使用泛型需要重新指定.
在實(shí)現(xiàn)中是不能使用泛型的.
限制泛型
通常我們在使用泛型時(shí)可以代替任意id類型,但有時(shí)我們使用泛型時(shí),希望對泛型進(jìn)行限制,我們希望泛型為UIView類簇中的類型.
很顯然這并不是我們希望看到了,所以我們可以對泛型進(jìn)行限制
@interface Stack <__covariant T : UIView *> : NSObject
- (void)push:(T)obj;
- (T)pop;
@end
使用上面聲明方法可以解決問題.
同時(shí)也可以限制泛型遵守指定的協(xié)議
@interface Stack <__covariant T : id<protocol>> : NSObject
- (void)push:(T)obj;
- (T)pop;
@end
協(xié)變 逆變
在上面我們開到了關(guān)鍵字__covariant
,下面就來看看這個(gè)關(guān)鍵字的作用
__covariant
:協(xié)變, 子類轉(zhuǎn)父類 :也就是將子類的指針賦值給父類(多態(tài)的延伸)
__contravariant
:逆變 父類轉(zhuǎn)子類:也就是將父類的指針賦值給子類(暫時(shí)沒有想到有什么作用,如果有哪位大佬知道歡迎指點(diǎn)一二).
在這里我們創(chuàng)建兩個(gè)類Animal
和他的子類Dog
.
//測試代碼
Stack <Dog *> *stack1 = [Stack new];
Stack <Animal *> *stack2 = [Stack new];
stack1 = stack2;
stack2 = stack1;
接下來我們分別來看看協(xié)變和逆變的特性.
// 逆變 父類指向子類
@interface Stack <__contravariant T : Animal *> : NSObject
// 協(xié)變 子類指向父類
@interface Stack <__covariant T : Animal *> : NSObject