——- android培训、IOS培训、期待与您交流! ———-
分类
分类(category)可以模块的方式向现有的类添加方法。
它提供了一种简单的方式,可以将类的定义模块化到相关方法的组或分类中。它还提供了扩展现有类定义的简便方式,并且不必访问类的源代码,也无需创建子类。
以一个Fraction类为例,除了将两个分数相加的add:方法外,还想要拥有将两个分数相减、相乘和相除的方法。
//Fraction类声明
#import <Foundation/Foundation.h>
@interface Fraction:NSObject
{
intnumerator;
intdenominator;
}
@property intnumerator,deniminator;
-(void) setTo:(int) nover:(int) d;
-(Fraction*) add:(Fraction*) f; // 声明分数的加法函数
-(void) print;
@end
现在,从接口部分删除add:方法,并将其添加到新分类,同时添加其他三种要实现的数学运算。看一下新MathOps分类的接口部分。
#import <Foundation/Foundation.h>
#import "Fraction.h"
@interface Fraction(MathOps)
- (Fraction*) add:(Fraction*) f; // 加法函数
- (Fraction*) mul:(Fraction*) f; // 乘法函数
- (Fraction*) sub:(Fraction*) f; // 减法函数
- (Fraction*) div:(Fraction*) f; // 除法函数
@end
//#import "Fraction.h" 这里既是分类接口部分的定义,也是对现有接口部分的扩展,所以必须包括原始接口部分
//@interfaceFraction(MathOps) 告诉编译器正在为FFrraaccttiioonn类定义新的分类,名称为MMaatthhOOppss。
可以在一个实现文件中定义Fraction.h
接口部分中的所有方法,以及MathOps分类中的所有方法。
也可以在单独的实现部分定义分类的方法。在这种情况下,这些方法的实现部分还必须找出方法所属的分类。与接口部分一样,通过将分类名称括在类名称之后的圆括号中来确定方法所属的分类,如下所示:
@implementation Fraction (MathOps)
//codeforcategorymethods
…
@end
关于分类的一些注意事项
A、 尽管分类可以访问原始类的实例变量,但是它不能添加自身的任何变量。如果需要添加变量,可以考虑创建子类。
B、 分类可以重载该类中的另一个方法,但是通常认为这种做法不可取。因为,重载之后,再不能访问原来的方法。
C、 可以拥有很多分类。
D、 和一般接口部分不同的是,不必实现分类中的所有方法。这对于程序扩展很有用,可以在该分类中声明所有方法,然后在一段时间之后才实现它。
E、 通过使用分类添加新方法来扩展类不仅会影响这个类,同时也会影响它的所有子类。
协议
协议的声明类似于类接口的声明,有一点不同的是,协议没有父类,并且不能定义成员变量。下面的例子演示了只有一个方法的协议的声明:
@protocol MyProtocol
-(void)myProtocolMethod;
@end
协议是多个类共享的一个方法列表,协议中列出的方法没有相应的实现。如果一个类采用MyProtocol协议,则必须实现名为myProtocolMethod的方法。
通过在@interface行的一对尖括号<…>内列出协议名称,可以告知编译器你正在采用一个协议。这项协议的名称放在类名和它的父类名称之后,如下所示:
@interface AddressBook: NSObject <myProtocol>
这说明,AddressBook是父类为AddressBook的对象,并且它遵守myProtocolMethod协议。在AddressBook的实现部分,编译器期望找到定义的myProtocolMethod方法。
如果采用多项协议,只需把它们都列在尖括号中,用逗号分开:
@interface AddressBook : NSObject<myProtocol,yourProtocol>
以上代码告知编译器AddressBook类采用myProtocolMethod和 yourProtocolMethod协议。这次,编译器将期望在AddressBook的实现部分看到为这些协议列出的所有方法的实现。
有关协议的注意事项:
A、如果一个类遵守某项协议,那么它的子类也遵守该协议。
B、协议不引用任何类,它是无类的(classless)。任何类都可以遵守某项协议。
C、通过在类型名称之后的尖括号中添加协议名称,可以借助编译器的帮助来检查变量的一致性,如下:
id <Drawing> currentObject;
这告知编译器currentObject将包含遵守Drawing协议的对象。如果向currentObject指派静态类型的对象,这个对象不遵守Drawing协议,编译器将给出warning。
再次提到id类型,如果向currentObject指派一个id变量,不会产生这条消息,因为编译器不知道存储在id变量中的对象是否遵守Drawing协议。
D、如果这个变量保存的对象遵守多项协议,则可以列出多项协议,如下:
id <Drawing,Drawing1> currentObject;
E、定义一项协议时,可以扩展现有协议的定义。以下协议
@protocol Drawing3D <Drawing>
说明Drawing3D协议也采用了Drawing协议。因此采用Drawing3D协议的类都必须实现此协议列出的方法,以及Drawing协议的方法。
F、分类也可以采用一项协议,比如:
@interface Fraction(stuff) <NSCopying,NSCoding>
此处,Fraction拥有一个分类stuff,这个分类采用了NSCopying和NSCoding协议。