概念
Category是基于原类进行扩展。虽然用子类也可以实现,但是Category更方便。
优点
- 将类的实现写个多个分类中
- 减少单个文件体积
- 多个开发者完成一个类
- 按需加载自己想要的类
- 不同功能放在不同的Category中
- 声明私有方法
- 模拟多继承(一个子类继承了多个父类的属性)
使用
创建Category。在调用处引用Category。
当在Category中重写原类方法的时候,调用时只会执行Category,不会执行原类中方法。
使用注意
- Category只能增加方法,不能增加成员变量。
- Category可以使用原类中的成员变量。
- Category重写原类方法的时候会造成覆盖。造成覆盖的原因是runtime在运行的时候查找会优先找到引入头文件的Category中方法。
- Category是运行时加载,不是编译时加载。从上一条也可以看出。
- 当原类,原类的父类,分类同时存在时,顺序为:分类,原类,原类父类。
成员变量与属性
属性(property),包含声明成员变量(也就是对应_name),以及相应的set
、get
方法。
我们都知道Category是不能添加属性,只能添加方法的,那么原因是:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0
long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
#endif
} OBJC2_UNAVAILABLE;
结合之前了解的runtime,来看类的构成。
再来看分类的构成。
struct category_t {
const char *name;
classref_t cls;
//对象方法
struct method_list_t *instanceMethods;
// 类方法
struct method_list_t *classMethods;
// 协议
struct protocol_list_t *protocols;
// 属性
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
很明显,不同于类中有ivars
的成员列表,分类中只有instanceProperties
的属性列表。因此不同于类中的属性会自动转化为成员变量和set
、get
方法,分类中的属性只是单纯的属性,不能生成对应的成员变量和方法。
- 分类的实现原理是将分类中的方法,属性,协议信息放在 category_t结构体中,然后将结构体内的方法列表拷贝到类对象的方法列表中。
- 分类中可以添加属性,但是并不会自动生成成员变量及set、get方法。因为底层的category_t结构体中并不存在成员变量。通过之前对对象的分析我们知道成员变量是存放在实例对象中的,并且编译的那一刻就已经决定好了。而分类是在运行时才去加载的,那么我们就无法在程序运行时将分类的成员变量中添加到实例对象的结构体中。因此分类中不可以添加成员变量.
网友评论