iOS學習筆記(01) - 泛型


  決定新開一坑,在不斷學習的同時分享自己的學習歷程給大家,既是對自己學習的記錄,又希望能對大家提供些微的幫助。

  

  這一篇文章主要來介紹泛型的意義、使用與聲明方法等。

1.泛型:限制類型

  1.1.泛型使用場景:

    1.在集合(數組NSArray、字典NSDictionary、集合NSSet)中使用泛型比較常見。

    2.當聲明一個類,但是類里面的某些屬性的類型不確定的時候,我們才使用泛型。

  1.2.泛型書寫規范

    在類型后面定義泛型:NSMutableArray<UITouch *> dataArray

  1.3.泛型修飾

    只能修飾方法的調用。

  1.4.泛型好處:

    1.提高開發的規范,減少程序員之間的交流。

    2.通過集合取出來的對象,可以直接當做泛型對象使用。這樣我們就可以直接使用.點語法。

  

2.代碼使用泛型:

  2.1.聲明一個泛型為NSString的數組

1 // 具體做法就是在 NSMutableArray 后帶一個 <NSString *> ,尖括號內部即為泛型類型
2 @property (nonatomic, strong, nullable) NSMutableArray<NSString *> *dataArray;

  2.2.當我們要給數組添加對象或取出對象的時候,系統就會自動提示應該傳入或者取出來的對象的類型,這個類型就是你剛才聲明的泛型類型

 1     // 1.沒有使用泛型
 2 //    [self.dataArray addObject:<#(nonnull id)#>];
 3 
 4     // 2.使用泛型
 5 //    [self.dataArray addObject:<#(nonnull NSString *)#>];
 6     
 7     // 3.添加不正確的類型,會出現警告
 8 //    [self.dataArray addObject:@1];
 9     
10     // 4.我們可以直接將集合中取出來的對象當做泛型使用
11     NSInteger length = [self.dataArray.firstObject length];

  

  基本上實現了使用泛型過程中可能出現的情況。

 

3.泛型的自定義

  剛才我們只是實現了系統類NSMutableArray的泛型。接下來我們要考慮下,我們怎么樣在我們自己的類中聲明一個泛型的屬性呢?

  為了這個目的,我們創建一個 Language 的類表示 “語言”。並且創建兩個 Language 的子類,分別叫 Java 和 IOS 。很明顯這兩個是“某一個類型的語言”。我們創建一個Person的類,給類聲明一個泛型,在類的 .h 文件中聲明一個聲明一個屬性,這個屬性表示這個人會的語言,即為 IOS 或者 Java 。那么我們有以下兩種聲明方式:  

1 // 語言
2 // 1.id:任何對象都能傳進來
3 //@property (nonatomic, strong, null_unspecified) id language;
4 // 2.Language:在外面調用的時候不能提示具體是哪種語言
5 //@property (nonatomic, strong, null_unspecified) Language *language;

  因為 Language 這個語言並不能代表這個 Person 究竟會什么語言,我們需要的屬性時 IOS 和 Java。這兩種都可以在調用的時候傳入 Java 和 IOS 兩種對象,但它們的缺點也非常明顯,若使用 id 則我們可以傳入任何對象,而不只是 Java 和 IOS ;使用 Language * 呢,我們沒有辦法在編譯的時候確定這個人究竟會什么語言,而只能在運行時判斷。有沒有辦法讓我們在編譯的時候就能知道 Person 具體會哪種 Language 呢?

  辦法就是泛型。

  聲明自定義類的泛型,我們需要做這樣一步:

1 // (給類)聲明一個泛型
2 @interface JTPerson<ObjectType> : NSObject

  看出區別了嗎?我們在 interface 類名之后加了一對尖括號 <> ,中間是 ObjectType 。這個就代表泛型。這樣我們在聲明屬性的時候就可以這么寫: 

1 @property (nonatomic, strong, null_unspecified) ObjectType language;

  也就是,我們現在不指定具體的類型,而在實例化這個類的時候確定這個泛型。若不確定,那么所有的 ObjectType 會自動變成 id 。像這樣:

 1     // iOS
 2     JTPerson<IOS *> *iOSP = [[JTPerson alloc] init];
 3      
 4 //    [iOSP setLanguage:@"123"];
 5     
 6 //    [iOSP setLanguage:<#(IOS * _Null_unspecified)#>];
 7     
 8 //    [iOSP setLanguage:[[Java alloc] init]];
 9     
10     // Java
11     JTPerson<Java *> *javaP = [[JTPerson alloc] init];
12     
13 //    [javaP setLanguage:@"123"];
14     
15 //    [javaP setLanguage:<#(Java * _Null_unspecified)#>];
16     
17 //    [javaP setLanguage:[[IOS alloc] init]];

  這樣,我們在聲明 Person這個類的時候,就順便聲明了這個類的泛型。這樣系統就會在你使用到泛型的屬性與方法的時候,自動提醒你聲明的泛型類型了。

 

4.泛型的協變與逆變

  首先我們來看一個方法:

1 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
2     
3     [touches anyObject];
4 }

  大家應該很熟悉這個方法了。這就是 UIViewController 繼承自 UIResponder 的方法,是點擊這個視圖控制器之后事件鏈的最后一環。這時候經過上面的學習,我們會看到這其中就使用了泛型: (NSSet<UITouch *> *) 。這樣我們點進集合 NSSet 會發現它是這樣寫的:

  前后我們都很清楚: interface:聲明;NSSet:集合;ObjectType:泛型;NSObject:是NSSet的父類,也是根類;后面尖括號中表示的是遵守的協議。

  那么 __covariant 代表什么?

  這里我們先來學習兩個單詞: covariant:協變的 contravariant:逆變的。

  那么 __covariant協變 代表什么?

 

  我們來試一下。在剛才的自定義類 Person 的泛型 ObjectType 前加這樣一條修飾: __covariant

1 @interface JTPerson<__covariant ObjectType> : NSObject

  然后在調用的時候,我們聲明兩個實例,泛型分別為父類 Language 和子類 IOS。這樣若泛型為 IOS 的 Person 想給泛型為 Language 的 Person 賦值,會怎么樣:

1     // 1.協變
2     // iOS
3     JTPerson<IOS *> *iOSP = [[JTPerson alloc] init];
4     
5     // Language
6     JTPerson<Language *> *p = [[JTPerson alloc] init];
7     
8     // 如果子類想給父類賦值,協變
9     p = iOSP;

  並沒有報錯。

  也就是說,若泛型為子類的對象想給泛型為父類的對象賦值的時候,我們需要在泛型前面添加 協變__convariant ,告訴編譯器,這樣轉換沒有問題。

  同理,__contravariant逆變就是若泛型為父類的對象想給泛型為子類的對象賦值的時候,我們需要在泛型前面添加 逆變__contravariant ,告訴編譯器,這樣轉換沒有問題。

  這就是協變與逆變的含義。

  協變與逆變本身是面向對象繼承的語言的一個特性,也並不只應用在泛型這里。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM