12.7 关于Category
Category如何实现的?
category的初始化
声明了一个类,添加了category,里边写了四个方法,分别是test,test1,test2,test3,
clang -rewrite-objc TestClass+Test1.m 后文件如下。
category的加载
runtime初始化的时候注册了一个有关于dyld的一个通知,收到这个通知的时候会根据情况调用map_images函数然后通过层层调用map_images->map_images_nolock->_read_image, image.png image.png 最终在read_image方法中存在如下代码: image.png 通过遍历,对类和元类分别进行类目信息的关联(addUnattachedCategoryForClass),和方法列表的重载(remethodizeClass)。Category维护这一个 NXMapTable类型的哈希表,表里存储这这个类所有的Category_t结构体,addUnattachedCategoryForClass的作用就是将我们查找到的Category_t,通过一个叫做 NXMapInsert的函数更新到这个哈希表中。
在remethodizeClass函数中调用了attachCategories函数,查找到Category的方法列表,属性列表,协议列表,然后通过对应的attachLists函数,添加到class对应的class_rw_t结构体中。 image.png
我们来看看attachLists这个函数,里边有两个最核心的函数memmove,memcpy。
memmove:将原来类中的信息列表在内存中向后移动,移动的大小就是分类中的信息所占大小
memcopy:将分类中的信息复制到上一步移动出来的空间。
Category和Extension有什么区别?
Extension也就是写在.m文件的@interface <类名>(),看似好像一个匿名的Category,实际上相差甚远。
Extension一般用于隐藏类的私有信息,是在编译期决定的,他就是类的一部分,里边声明的一些属性等信息都会成为之前说过的class_ro_t结构体中的信息,是在编译期就做好了内存分配的。
Category是在运行时动态添加到类中的。
怎么调用原来类中被category覆盖掉的方法?
image.png如何给Category添加属性?
类目category 和 associative 是OC扩展机制中得两个特性。
category用来扩展方法,associative用来扩展属性
前者我们都经常使用不再赘述,后者可能用的相对较少,要使用后者必须使用<objc/runtime.h>的头文件,然后使用objc_getAssociatedObject以及objc_setAssociatedObject方法,进行属性的添加
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
object 参数作为待扩展的对象实例一般写在类目中就是self,key作为该对象实例的属性的键,而value就是对象实例的属性的值,policy作为关联的策略
关联的策略其实就是我们添加属性时候的assign strong等等 他的枚举类型包括如下:
enum {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
还有另外一个方法
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
另外,objc_removeAssociatedObjects可以删除指定对象实例的所有扩展属性
这个变量被添加到哪儿了?是var列表么?
@property的作用是生成一个_<属性名字>成员变量,生成set<属性名字>的方法和get<属性>的方法。
但是当你在category声明了一个@property时,因为一个类的成员变量是在编译期就决定的一个常量,无法动态修改他的成员变量列表,所以这里的@property只是声明了一个属性,并不能生成成员变量和get,set方法。需要手动实现set和get方法。这里就是上边通过关联对象实现的。
那么通过属性关联添加的属性是被维护在了一个全局的哈希表中,叫做AssociationsHashMap,所以我们获取这个属性的值时时从这个全局的哈希表中获取的。