美文网首页
12.7 关于Category

12.7 关于Category

作者: 哈库呐玛塔塔__ | 来源:发表于2020-05-15 00:32 被阅读0次

    Category如何实现的?

    category的初始化

    声明了一个类,添加了category,里边写了四个方法,分别是test,test1,test2,test3,
    clang -rewrite-objc TestClass+Test1.m 后文件如下。

    1.可以看到编译后文件声明了一个category_t的结构体。包括类的名字(注意不是分类名字),要扩展的类对象,实例方法列表,类方法列表,属性列表(可以通过关联对象添加属性的原因),甚至还有协议列表,但是没有成员变量Ivar,因为一个类的ivar是在编译期就决定的。category虽然在编译器进行了初始化,但是它是在运行时才将其信息重新组合到类下的。 image.png

    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:将分类中的信息复制到上一步移动出来的空间。

    系统是在运行时将分类中对应的实例方法、类方法等插入到了原来类或元类的方法列表中,且是在列表的前边!所以,方法调用时通过isa去对应的类或元类的列表中查找对应的方法时先查到的是分类中的方法!查到后就直接调用不在继续查找。这即是’覆盖’的本质! image.png

    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,所以我们获取这个属性的值时时从这个全局的哈希表中获取的。

    相关文章

      网友评论

          本文标题:12.7 关于Category

          本文链接:https://www.haomeiwen.com/subject/kntgnhtx.html