分类

  在Objective-C中,除了通过新建子类的方式来向类添加新方法外,还可以通过分类的方式。分类提供了一种简单的方式,将类的定义模块化到相关方法的组或分类中,它还提供了扩展现有类定义的简便方式,并且不需要访问类的源代码,也无须创建子类。

  比如:

//SomeClass+MathOps.h
#import "SomeClass.h"
@interface SomeClass (MathOPs)
-(SomeClass *) add: (SomeClass *)s;
-(SomeClass *) sub: (SomeClass *)s;
-(SomeClass *) div: (SomeClass *)s;
-(SomeClass *) mul: (SomeClass *)s;
@end

  这里不用列出父类,也不用向编译器告知实例变量,因为从import的文件的接口部分已经这样做了。

  可以将所有的方法的定义放在一个实现部分。也就是说,可以在一个实现文件中定义SomeClass.h接口部分中的所有方法,以及MathOps分类中的所有方法。或者,在单独的实现部分定义分类的方法,这种情况下,这些方法的实现部分必须找出方法所属的分类。如@implementation SomeClass (MathOps) 这样。

  如果将分类放到一个主类定义文件中,那么这个类的所有用户都将访问这个分类中的方法。如果不能直接修改原始的头文件,则只能将分类单独保存文件。

 

类的扩展

创建一个未命名的分类,且在()内不指定名字,这是一种特殊情况,这种特殊语法定义为类的扩展。定义一个像这样的未命名分类时,可以通过定义附加的实例变量来扩展类,这在命名的分类中是不允许的。未命名分类中声明的方法需要在主实现区域实现,而不是在分离的实现区域中实现。如果没有实现未命名分类的接口部分列出的全部方法,编译器会发出警告。

比如,在一个类的实现文件中:

//SomeClass.m
#import "SomeClass.h"
//类的扩展
@interface SomeClass ()
@property int pp;
-(void)someFunc;
@end

//--------------------------
@implementation SomeClass
@synthesize pp;
.....
@end

  通过添加一个新的实例变量和方法扩展了这个类,并合成了存取方法。

  未命名分类是非常有用的,因为它们的方法都是私有的。如果需要写一个类,其数据和方法仅供类本身使用,未命名分类比较合适。

分类的注意事项:

  分类可以覆写该类中的另一个方法,不过这是拙劣的设计,因为覆写一个方法之后,再也不能访问原来的方法。如果需要覆写,正确的选择是创建子类,子类中覆盖父类的方法,此时仍然可以通过super来调用父类原方法。

  可以拥有许多分类,如果一个方法定义在多个分类中,该语句不会指定使用哪个分类。

  通过分类添加新方法来扩展类不仅会影响这个类,同时也会影响它的所有子类。

 

协议

协议是多个类共享的一个方法列表,协议中列出的方法并没有相应的实现,计划由其他人来实现的。协议提供一种方式,用指定的名称定义一组多少有点相关的方法。这些方法有的是必须实现的(@required),有的是可选的(@optional)。

  定义协议是使用@protocol指令,后面加上协议名称。

  当采用某协议时,在@interface行最后的一对<>中列出协议名称即可,同时在实现部分至少实现协议的必须实现方法。

  比如:@interface SomeClass:SuperClass <SomeProtocol>

  协议是无类的,它不引用任何类,任何类都可以遵守某个协议。可以使用comformsToProtocol:方法检查一个对象是否遵循某协议。

 

代理

  协议也是一种两个类之间的接口定义。定义了协议的类可以看做是将协议定义的方法代理给了实现它们的类,这样,类的定义可以更为通用,因为具体的动作由代理类来承担,来响应某些事件或者定义某些参数。Cocoa和iOS非常依赖代理这个概念。比如UITableView类,这个类不清楚表格的标题是什么,需要包含多少区块或是包含多少行,填充表格的内容是什么。所以,代理定义了一个UITableViewDataSource协议,如果它需要信息,比如表格中每个区块有多少行,它就会调用类中实现协议的相关方法。UITableView类还定义了其他的协议如UITableViewDelegate,协议还定义了一些方法,如表格某行被选中需要怎样,UITableView不知道要做什么,所以将这个代理给了实际用户。

 

非正式协议

  非正式协议实际上是一个分类,列出了一组方法但是没有实现他们。每个人都继承相同的根对象,因此,非正式协议通常是为根类定义的。有时,非正式协议又被称为抽象协议。因为非正式协议本身不是协议而是分类,所以编译器不提供协议相关的帮助,不会存在遵守或者不遵守非正式协议或者由编译器测试这样的概念。

 

合成对象

  除了通过派生子类和分类可以扩展类定义以外,还有一项技术可以定义一个类包含其他类的一个或多个对象,这个新类的对象就是合成对象,因为它是由其他对象组成的。

  通过派生子类扩展父类,子类就集成了父类所有的实例变量和方法,一些情况下,有些方法并不适用于子类,然而子类的用户却有可能会访问它们。作为创建子类的替代方式,可以定义一个新类,它包含要扩展类的实例变量,然后,只需在新类中定义适合该类的方法。比如:

@interface NewClass:NSObject
{
  SomeClass *s;  
}
-(void)newFunc;
@end

  这里需要注意,与子类扩展不同,子类版本允许直接访问父类的方法,这里NewClass的对象不能直接访问SomeClass的方法了,而是在自己的方法里比如newFUnc里边,通过实例变量s来访问那些可以被用到的方法。记住,这里在初始化时,需要覆写init或者自定义初始化方法来给s分配空间。