iOS類別和類擴展

在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方法。包含之後就一切正常了。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章