1.概念:Category是Objective-C 2.0 之后添加的语言特性,它可以为已经存在的类添加方法
2.作用:
2.1:可以减少单个文件的体积
2.2.可以把不同的功能组织到不同的 Category 中
2.3.可以按需加载
2.4.声明私有方法
2.5.把 framework 的私有方法公开
3.源码:源码链接:https://github.com/RetVal/objc-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);
};
4.Category和extension(扩展)的区别
4.1.extension:
类的一部分
编译时期决定
隐藏类的私有属性
对象的内存布局已确定
4.2.Category:
运行时期
不能添加变量
5.category从编译到运行时期的过程
5.1.编译时期:创建一个category,通过命令:clang -rewirte-objc category.m,可以看到它被加载到了__DATA数据段的__objc_catlist中
5.2.运行时期调用顺序:
5.2.1.dyld_start:dyld是苹果的动态加载器,用来加载动态库,其中包括libobjc(OC和runtime)
可以通过打符号断点看到
5.2.2._objc_init:入口函数,读取Mach-o文件对应的Segment section,并根据其中的数据代码信息,完成为OC的内存布局,以及初始化runtime相关的数据结构
5.2.3.map_images:dyld将image(Mach-o格式的二进制文件)加载进内存
5.2.4.load_images:dyld初始化image,load方法也在这个时候调用
5.2.5.umap_image:dyld将image移除内存
5.2.6.read_image:读取oc相关的section,通过读取seciton段来进行初始化
map_images_nolock函数中找到_read_images
5.2.7._getObjc2CategoryList:读取__objc_catlist
5.2.8.addUnattachedCategoryForClass:把分类加载到类当中
5.2.9.NXMapInsert:cats(Category)和cls(类)建立关联
5.2.10.remethodizeClass:对当前cls 的内存布局进行重新排列
5.2.11.attachCategories:把当前的cats附加到cls中
5.2.12.prepareMethodLists:准备当前类的方法列表
5.2.13.attachLists:通过memmove和memcpy,把分类的方法添加到原来的方法列表当中
6.总结:
6.1.通过5.2.7_getObjc2CategoryList读取__objc_catlist secition段记录中所有的category,存放到category_t 数组中
6.2.依次读取category_t数组
6.3.cat和cls进行映射
6.4.通过remethodizeClass修改method_list结构,这里涉及到memove和memcpy
7.动态添加属性
7.1.AssociationsManager中存放了一个map(AssociationsHashMap),map中的key是当前对象地址的按位取反,value是一个迭代器,也是一个map,迭代器map中的key是自定义的标识,value是ObjcAssociation(用户自定义的,用来存储当前的关联属性
)
7.2.关联对象的释放是在源对象被销毁时释放(dealloc),通过对象的指针来释放关联对象,具体调用的是_object_remove_assocations中的ReleaseValue
8.load方法:
8.1.一个类的load方法在所有的父类都调用完之后调用
8.2.category的调用load方法在类的调用load方法之后调用
8.3.category之间的调用顺序:通过dyld调整调用顺序和编译顺序有关