10.Object-C--淺談Category分類


 

 

  今天呢,我又要開啟我的bibi模式了,首先我給大家出個問題:假如有一個需求是讓你擴充類,這時候你會怎么做?

  可能我們想到最多的就是使用繼承。其實啊!在OC中有一種除了繼承之外的另一種方法:分類(Category)。

  那什么是分類呢?

  簡單來說,就是在不改變原先類前提下,我們可以添加咱們自定義的方法,這樣和同事合作開發的時候,是不是順暢的多啦?但是使用分類的時候不能向原先類中增加成員變量,分類方法實現中可以訪問原來類中的成員變量。

  下面我先舉一個簡單的例子:

  現在有一個Person類,里面有一個study方法,現在想在不改變Person類的基礎上添加一個test方法。那么我們就可以新建一個分類如下:

 

   示例代碼如下:

 1 //Person.h
 2 #import <Cocoa/Cocoa.h>
 3 
 4 @interface Person : NSObject  5 - (void)study;  6 @end
 7 
 8 //Person.m
 9 
10 #import "Person.h"
11 
12 @implementation Person 13 - (void)study 14 { 15     NSLog(@"Person 類中的study方法"); 16 } 17 @end
18 
19 //分類Person+QYMa.h
20 #import "Person.h"
21 
22 @interface Person (QYMa) 23 - (void)test; 24 @end
25 
26 //Person+QYMa.m
27 #import "Person+QYMa.h"
28 
29 @implementation Person (QYMa) 30 - (void)test 31 { 32     NSLog(@"Person+QYMa 中的test方法"); 33 } 34 @end
35 //main方法 測試
36 #import <Foundation/Foundation.h>
37 #import "Person.h"
38 #import "Person+QYMa.h"
39 
40 int main(int argc, const char * argv[]) { 41  @autoreleasepool { 42       
43         Person *p = [[Person alloc]init]; 44  [p study]; 45  [p test]; 46         NSLog(@"Hello, World!"); 47  } 48     return 0; 49 }

  測試結果:

   通過這個示例,咱們來細說一下分類的、聲明實現格式:

1 @interface 類名(分類名稱)  //建議分類名稱為模塊名稱 2 //方法的聲明
3 @end
1 @interface 類名(分類名稱)  //建議分類名稱為模塊名稱 2  //方法的聲明
3 @end

 

  接下來咱們來說說,分類一般的使用場景

  • 在定義類的某些情況下(例如需求變更),你可能想要為其中的某個或幾個類中添加新的方法。
  • 一個類中包含了許多不同種類的方法需要實現,而這些方法需要不同團隊的成員實現。
  • 在使用基礎類庫的類時,有可能希望這些類實現一些自己需要的方法。

  在這里呢,需要說一下如果出現相同方法名的方法的時候,優先在分類中找,然后在原來類中找,最后再父類。分類可以實現原來類中的方法,相當於覆蓋了原來的方法,使原來類中的方法失效。所以不建議同名。那么還有一種情況:假如有兩個分類同名方法都有實現的話要怎么調用?這個就和文件編譯順序有關,最后編譯的會覆蓋之前的,所以執行的是最后編譯的那個一個。

  好接下來,我們舉一個擴充NSString類方法的例子:要求實現:計算某個字符串中阿拉伯數字的個數。

  我們先新建一個分類:

  示例代碼如下:

 1   //NSString+Number.h
 2  #import <Foundation/Foundation.h>
 3   
 4   @interface NSString (Number)
 5   + (int)numberCountofString:(NSString *)str;
 6   - (int)numberCount;
 7   @end
 8   
 9   //NSString+Number.m
10  #import "NSString+Number.h"
11 
12  @implementation NSString (Number)
13 + (int)numberCountofString:(NSString *)str
14 {//1.定義變量計算數字的個數
15 //    int countNum = 0;
16 //    for (int i = 0; i < str.length; i++)
17 //    {
18 //        unichar c = [str characterAtIndex:i];
19 //        if (c >= '0' && c <= '9')
20 //        {
21 //            countNum++;
22 //        }
23 //    }
24 //    return countNum;
25     return [str numberCount];
26 }
27 
28  - (int)numberCount
29 {
30      int count = 0;
31     for (int i = 0; i < self.length; i++)
32     {
33         unichar c = [self characterAtIndex:i];
34          if (c >= '0' && c <= '9')
35         {
36             count++;
37         }
38     }
39     return count;
40  }
41  @end
42  //main 測試代碼
43  #import <Foundation/Foundation.h>
44  #import "NSString+Number.h"
45  int main(int argc, const char * argv[]) {
46     @autoreleasepool {
47          
48      int num =  [NSString numberCountofString:@"1213Qweq1"];
49      //  int num = [@"1213Qweq1" numberCount];
50         NSLog(@"字符串中阿拉伯數字的個數:%d",num);
51     }
52     return 0;
53 }

 測試結果如下:

 

  通過這兩個例子,估計分類我們也理解的差不多了。下面咱們來說說繼承和分類的區別

  Category是對一個功能完備的類的一種補充,就像是一個東西的主要基本功能都完成了,可以用category為這個類添加不同的組件,使得 這個類能夠適應不同情況的需求(但是這些不同需求最核心的需求要一致)。找個就像你已經有了一輛能夠開動的汽車一樣,我們可以用Category為你的汽 車添加各種之前沒有的功能,最后讓這輛汽車變成超級跑車一樣。當某個類非常大的時候,Category可以按不同的功能將類的實現分在不同的模塊中實現。

  繼承則是都可以完成上面的工作,但是繼承有很大的代價問題,一是通過繼承來進行擴展是一種耦合很高的行為,對父類可以說是完全依賴;二是繼承由於 對父類依賴,所以開發代價相對大,要求對父類的工作流程相對熟悉;三是繼承體系如果太復雜會導致整個系統混亂,難以維護。所以在能夠分類的時候,就千萬不要使用繼承。什么情況才是迫不得已要使用繼承呢?那就是如果你既想提供一系列接口的定義,同時又想提供一些但是又不能提供全部的實現的 時候,這種情況就要使用繼承了。

 

  雖然Category可以訪問類的實例變量,去不能創建新的實例變量,如果要創新的實例變量,請使用繼承。

  在Category中,不提倡對原有方法進行重載。原因非常簡單,在Category中進行重載,無法對原方法進行訪問,而繼承中可以使用super。如果真的需要對原方法進行重載,請考慮繼承。

  一個類可以定義多個Category,但是如果不同Category中存在相同方法,編譯器無法決定使用哪個Category;
  在定義Category時,我們可以僅僅給出方法定義,而不需要給出具體的實現。這在程序增量開發時是非常有幫助的;
  Category是可以被繼承的。在某個父類中定義了Category,那么他所有的子類都具有該Category;
  在需要為某個類創建私有成員方法時,也用Category的方式來實現。
  Category不能完全代替子類,有以下幾個最大的缺點:
  當在Category中覆蓋一個繼承的方法,在Category中的方法可以通過向super類發送一個消息來調用被繼承的方法。但是,如果Category中覆蓋的那個方法已經在這個類的其它Category定義過了,則之前定義的方法將沒有機會被程序調用。
  在Category中無法確定其能夠可靠的覆蓋某個方法,而這個方法已經在其它的Category中定義過。這個問題在使用Cocoa框架時尤其 突出。當你想覆蓋某個框架已經定義好的方法時,該方法已經在其它Category中實現,這樣就無法確定哪個定義和實現會被最先使用,帶來很大的不確定 性。
  如果你重新覆蓋定義了一些方法,往往會導致這個方法在整個框架中實現發生了變化。

  所以鑒於以上原因,在選擇繼承還是分類的時候,多考慮一些因素。祝大神們都寫出更好的代碼!

  好啦!今天的bibi,就到此結束啦!感慨一下,外面還在下雨。-_-#。還能不能愉快的玩耍了?

 


免責聲明!

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



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