文章目录
前言
上一篇章我们探究了类与对象的底层,这一篇我们探究一下分类,扩展与关联对象
一、分类实现原理
首先我们知道扩展是在编译时就被添加到类中,而分类则是在运行时才被整合到类信息中,因此我们探究一下Category
编译之后的底层结构struct category_t
我们查看一下它的源码
发现里面存在实例方法列表,类方法列表,实例属性列表与类属性列表
由此我们得出两个结论:
- 分类可以声明属性,也会生成该属性对应的
get
和set
的声明,但没有去实现该方法 - 由于结构体中没有成员变量列表,因此不能声明成员变量
二、分类加载流程
- 在编译阶段将分类中的方法、属性等编译到一个数据结构
category_t
- 将分类中的方法、属性等合并到一个大数组中,后面参加编译的分类会在数组的前面
- 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
也就是说当分类中的方法与原始类中的方法重名时,会先去调用分类中实现的方法
三、扩展
Extension是Category的一个特例。类扩展与分类相比只少了分类的名称,所以称之为"匿名分类"
扩展中声明的属性以及方法会在编译阶段直接整合到类中,常用于声明私有属性与方法
四、类别与类扩展的区别
- 类别原则上只能添加方法(可以通过关联对象的方法添加属性,但无法访问成员变量)
- 扩展即可以添加方法又可以添加成员变量(或是属性)
- 扩展中的方法没有被实现会报警,类别中的不会,因为扩展在编译阶段就被添加到类中,分类在运行时才被添加到类中。而对于方法是否实现的检查一般是在编译时完成的
- 定义在.m文件中的扩展是私有的,定义在.h文件中的扩展是公有的。因此为在.m文件中实现扩展可以很好地实现私有属性与方法
五、关联对象
通过关联对象给分类添加属性
动态添加
bash
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
-
参数一:
id object
: 给哪个对象添加属性,这里要给自己添加属性,用self。 -
参数二:
void * == id key : key
值,根据key
获取关联对象的属性的值,在objc_getAssociatedObject
中通过次key
获得属性的值并返回。 -
参数三:
id value :
关联的值,也就是set方法传入的值给属性去保存。 -
参数四:
objc_AssociationPolicy policy :
策略,属性以什么形式保存。
取值
bash
objc_getAssociatedObject(id object, const void *key);
-
参数一:
id object
: 获取哪个对象里面的关联的属性。 -
参数二:
void * == id key
: 什么属性,与objc_setAssociatedObject
中的key
相对应,即通过key
值取出value
。
移除关联对象
bash
- (void)removeAssociatedObjects
{
// 移除关联对象
objc_removeAssociatedObjects(self);
}
应用
分类.h
分类.m
main函数
输出:
总结
- 由于分类没有成员变量列表,所以无法添加成员变量。与此同时他有属性列表,但只会声明属性,不会去实现set与get方法 。如果要在分类中使用属性就需要使用关联对象
- 分类在运行时被整合到类中,扩展在编译时被整合到类中,因此分类中方法不实现不会报警,扩展会
- 扩展常用于声明私有属性与方法
- 如果分类中的方法与类中方法重名,分类中的方法会替代类中的方法