分类(cagegory)
cagegory可以叫做分类或者类别,是Objective-C 2.0之后添加的语言特性。
主要特点及作用:
- 有名字,一般根据具体作用来取名,如NSAarry(Safe)
- 一般以独立文件形式存在,也可以放在本类中
- 如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。
- 同名分类方法生效取决于编译顺序
- 运行时决议
- 可以模拟多继承
-
可以给本类扩展方法
(避免使用继承,便于代码复用)
- 可以用于分解体积庞大的类文件
- 可以创建对私有方法的前向引用
本类.m中实现私有方法-methodA,.h中不需要申明,在该类的Category的.h中声明-methodA。在要调用-methodA的类import这个Category,再去运行,就不会报错,而且-methodA的私有方法也执行了
-
可以添加非正式协议
(所谓的非正式协议就是类别,即凡是NSObject或其子类的类别,都是非正式协议。因为几乎所有的OC的类都是NSObject的子类,所以所有的类只要import了这个头文件,就可以调用testMethod,这就是所谓的非正式协议)
-
可以添加属性@property,但不会自动生成getter/setter
为该属性添加@dynamic可以自动生成该属性的getter/setter,但依然没有合成成员变量
可通过关联对象(objc_getAssociatedObject\objc_setAssociatedObject)来合成一个成员变量,但这种做法要尽量避免。原因就在于分类无法自己合成成员变量。使用关联对象的方式,代码量大,且在内存管理上容易出错。正确的做法是把属性放在主类中。分类的目的是在于扩展功能,而非封装数据。
关联对象由AssociationManager管理并在AssociationHashMap存储。所有对象的关联内容都存在用一个全局容器中,和宿主类无关。
/**
* Returns the value associated with a given object for a given key.
*
* @param object The source object for the association.
* @param key The key for the association.
*
* @return The value associated with the key \e key for \e object.
*
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Sets an associated value for a given object using a given key and association policy.
*
* @param object The source object for the association.
* @param key The key for the association.
* @param value The value to associate with the key key for object. Pass nil to clear an existing association.
* @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*
* @see objc_setAssociatedObject
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
-
可以将Framework中的私有方法公开
(Framework私有方法 看不见声明调用不了 如果知道可以通过分类声明这个方法暴露出来;然而苹果对私有API管制较严,所以要根据实际情况慎用)
同名方法的调用顺序: 1.分类 2.本类 3.父类
扩展(extension)
extension其实也是分类的一种,只是没有名字,所以也可以叫做匿名分类。 主要特点及作用:
- 没有名字,如AClass()
- 一般存在于本类中,没有独立的文件
- 可以声名私有属性、私有方法、(私有)成员变量
-
编译时决议
必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension
两者区别
- 分类中原则上只能增加方法;扩展中可以增加方法、属性、实例变量
- 分类有名字;扩展没名字
- 分类可以有独立的文件(独立的实现);扩展一般是和主类存在同个类文件中的
-
分类中的方法实现在运行时决议;类扩展的方法在编译时决议
扩展是在编译阶段被添加到类中,而分类是在运行时添加到类中。
- 扩展中声明的方法没被实现,编译器会报;但是分类中的方法没被实现编译器是不会有任何警告的。
可以分别用来做什么
分类:
- 给类扩展方法
- 分解体积庞大的类文件
- 模拟多继承
- 把Framework私有方法公开
- 声明私有方法
扩展:
- 声明私有属性\方法\成员变量
分类有哪些局限性
分类:
- 不能声名属性
- 名称冲突,即当类别中的方法与原始类方法名称冲突时,类别具有更高的优先级。
- 如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法
扩展:
- 不能为系统类添加扩展
分类的结构体里有哪些成员
1
2
3
4
5
6
7
8
9
10
// Category 是表示一个指向分类的结构体的指针,其定义如下:
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分类名
char *class_name OBJC2_UNAVAILABLE; // 分类所属的类名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分类所实现的协议列表
}