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,就到此结束啦!感慨一下,外面还在下雨。-_-#。还能不能愉快的玩耍了?

 

posted @ 2015-04-03 22:12  Qingyun_Qearl  阅读(3713)  评论(0编辑  收藏  举报