在iOS中,有一種機制可以使用戶在沒有遠嗎的情況下擴展類的功能,但不是通過繼承,這就是類別。iOS中沒有類似C++中可以定義私有方法和私有變量的關鍵字,要定義私有方法和私有變量,可以用類擴展來實現。
類別
類別在不需要繼承的情況下可以擴展類的功能。但類別不能添加類的屬性和私有變量。類別可以用來擴展Cocoa中類的方法,也可以用來擴展用戶自己的類中的方法。當我們查看系統頭文件的時候能發現類似@interface
NSMutableArray (NSExtendedMutableArray)的類定義,其實這就是類別的定義形式。例如下面的代碼,定義了一個NSString的類別,是用來做Base64的編碼和解碼的。
[cpp] view plaincopy
@interface NSString (Base64)
-(NSString *)encodeBase64;
-(NSString *)decodeBase64;
@end
類別的定義和類的定義有相似之處,都是用關鍵字@interface和類名來定義,不同之處在於類別的定義是在類名之後不是類所繼承的父類,而是用括號括起來的類別名。@end之前的方法定義和類中方法的定義是一樣的。不過,在類別中,不能定義屬性。類別中的方法與原類中的方法的使用是完全一致的,沒有任何差別。所有NSString的子類也都能使用類這兩個類別中的方法。
在使用類別的時候,類別中的方法命名特別重要。如果類別中的方法名與原類中的方法名重名了,在蘋果開發者文檔中的描述是,當方法重名的時候,在運行的時候不知道會調用哪個方法。實際上,這個應該是有規則可循的。我建了一個工程,給NSString增加了一個類別,裏面重寫了length和substringFromIndex方法。length方法是NSString的方法,substringFromIndex是NSString的一個類別裏的方法。當我調用者兩個方法時,發現調用length的時候返回的是系統的那個調用,而不是我自己實現。而當調用substringFromIndex時,調用的則是我實現的方法。於是我推斷當系統類中的方法名與自己定義的類別裏的方法重名時,會調用系統的方法名,而當自定義類別中的方法名與系統類別中的方法重名時,會使用自定義類別中的方法的實現。爲了驗證這一推斷,我又繼續增加了NSArray的類別來進行測試,測試結果正如我推斷的一樣。即使是這樣,我們也不能確定這個結論就是正確的,還有待進一步的驗證。
在上面的Base64類別中,增加兩個方法
[cpp] view plaincopy
-(NSUInteger)length;
-(NSString *)substringFromIndex:(NSUInteger)from;
[cpp] view plaincopy
實現這兩個方法
[cpp] view plaincopy
-(NSUInteger)length
{
return 40;
}
-(NSString *)substringFromIndex:(NSUInteger)from
{
return @"sub string";
}
在實現這兩個方法的時候,length方法會有一個警告,說這是原類中的方法,而第二個方法卻沒有,因爲它是NSString的一個類別中的方法。將這兩個方法做自己的實現
現在來試着調用一下
[cpp] view plaincopy
NSString *title = @"標題";
NSLog(@"title length:%d", [title length]);
NSLog(@"sub string from index 1: %@", [title substringFromIndex:1]);
輸出結果是:
[cpp] view plaincopy
2013-08-16 00:19:30.678 CategoryTest[12088:c07] title length:2
2013-08-16 00:19:30.679 CategoryTest[12088:c07] sub string from index 1: sub
string
length的結果沒有問題,而substring的方法就是調用了咱們實現的類別裏的方法。
再來看看NSArray的類別定義
[cpp] view plaincopy
@interface NSArray (ArrayTest)
- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;
- (id)lastObject;
@end
前面兩個方法是NSArray自帶的方法,後面一個方法是NSArray類別裏的方法,將他們用自己的方式實現
[cpp] view plaincopy
@implementation NSArray (ArrayTest)
- (NSUInteger)count
{
return 4;
}
- (id)objectAtIndex:(NSUInteger)index
{
return nil;
}
- (id)lastObject
{
return [self objectAtIndex:0];
}
@end
現在我們來調用一下這幾個方法
[cpp] view plaincopy
NSArray *array = [NSArray arrayWithObjects:@"object 1", @"object 2", nil];
NSLog(@"array count: %d", [array count]);
NSLog(@"array object at index 0:%@", [array objectAtIndex:0]);
NSLog(@"array last object:%@", [array lastObject]);
輸出結果如下:
[cpp] view plaincopy
2013-08-16 00:19:30.680 CategoryTest[12088:c07] array count: 2
2013-08-16 00:19:30.681 CategoryTest[12088:c07] array object at index 0:object
1
2013-08-16 00:19:30.681 CategoryTest[12088:c07] array last object:object 1
上面的推斷是基於實現的是系統類的類別,如果是自己的類的類別呢,是不是也跟系統的一樣。經過測試,結果稍有不同。當類別中的方法名與類中的方法重名時,調用的是類別中的方法。如果多個類別中有相同的方法,這個就跟類別的編譯順序有關了,誰最後編譯就調用誰的方法。我試着改變過不同類別文件的編譯順序,發現方法的調用也跟着變了。這個自己可以寫個類測試一下。
類擴展
類擴展跟類別的定義有點像,類擴展有點像無名的類別。如下定義
[cpp] view plaincopy
@interface Person ()
@property (nonatomic, strong) NSString *address;
@end
上面的代碼是定義了一個Person類的類擴展,它與類別的不同之處在於,括號裏不需要寫名字。同時也可以在類擴展中定義屬性以及私有變量。另一個不同之處在於,類擴展必須與類定義以及類的實現同時編譯,也就是說,類擴展只能針對自定義的類,不能給系統類增加類擴展。類擴展定義的方法必須在類的實現中進行實現。如果單獨定義類擴展的文件並且只定義屬性的話,也需要將類實現文件中包含進類擴展文件,否則會找不到屬性的set和get方法。
在Person的類實現Person.m中,需要將Person_PersionExtension.h包含進行,否則是無法使用address屬性的,測試的時候會崩潰,因爲找不到setAddress方法。包含之後就一切正常了。
iOS類別和類擴展
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章
被誤解的MVC和被神化的MVVM
苹果真的只是平果
2018-08-25 18:09:55
iOS property兩種實現方法區別的簡單介紹
苹果真的只是平果
2018-08-25 18:09:55
Cocoa Touch事件處理流程--響應者鏈
苹果真的只是平果
2018-08-25 18:09:48
iOS MVC和MVVM簡單介紹
苹果真的只是平果
2018-08-25 18:09:47
iOS兩種官方的單例模式寫法
苹果真的只是平果
2018-08-25 18:09:47
iOS開發中static變量的三大作用
苹果真的只是平果
2018-08-25 18:09:45
一種iOS7 設置隱藏狀態欄的方法
苹果真的只是平果
2018-08-25 18:09:45
iOS內存管理之@property屬性詳解
苹果真的只是平果
2018-08-25 18:09:43