美文网首页
聊一聊Category

聊一聊Category

作者: 晨阳Xia | 来源:发表于2020-12-24 16:46 被阅读0次

Category的实现原理

Category中对象方法,在程序运行过程中,都会进入类的对象方法列表中。实例变量都是在自己的类对象的方法列表中查找方法。
Category中类方法,在程序运行过程中,都会进入元类的对象方法列表中。实例变量都是在自己的元类对象的方法列表中查找方法。

Category 源码

将Objective-C编译成C++代码的命令

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test.m -o Person+Test.cpp

编译前:

@interface Person (Test)<NSCopying>


@property (nonatomic, strong) NSString *xsysString;

- (void)test;

@end

@implementation Person (Test)

- (void)run {
    NSLog(@"test");
}

+ (void)test {
    
}

@end

编译后:

struct _category_t {
    const char *name;                                 // 类名
    struct _class_t *cls;                             // 
    const struct _method_list_t *instance_methods;    // 实例方法
    const struct _method_list_t *class_methods;       // 类方法
    const struct _protocol_list_t *protocols;         // 协议
    const struct _prop_list_t *properties;            // 属性
};

// 实例方法结构体
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"run", "v16@0:8", (void *)_I_Person_Test_run}}
};

// 类方法结构体
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"test", "v16@0:8", (void *)_C_Person_Test_test}}
};

// 协议
static const char *_OBJC_PROTOCOL_METHOD_TYPES_NSCopying [] __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "@24@0:8^{_NSZone=}16"
};

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"copyWithZone:", "@24@0:8^{_NSZone=}16", 0}}
};

struct _protocol_t _OBJC_PROTOCOL_NSCopying __attribute__ ((used)) = {
    0,
    "NSCopying",
    0,
    (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying,
    0,
    0,
    0,
    0,
    sizeof(_protocol_t),
    0,
    (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCopying
};
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_NSCopying = &_OBJC_PROTOCOL_NSCopying;

static struct /*_protocol_list_t*/ {
    long protocol_count;  // Note, this is 32/64 bit
    struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1,
    &_OBJC_PROTOCOL_NSCopying
};

// 属性
static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"xsysString","T@\"NSString\",&,N"}}
};

// 分类结构体赋值
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "Person",                                                                               // 变量名赋值           
    0, // &OBJC_CLASS_$_Person,  
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test,      // 实例方法结构体
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test,         // 类方法结构体
    (const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_Test,           // 协议                                              
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Test,                        // 属性
};

当程序一编译的时候,cagtegory中的数据会编译成_category_t结构体,数据存储在当前结构体内。

runtime中Category源码解读

过程分析:
控制编译顺序:compile source中的文件顺序就是编译顺序
最后面参与编译的分类会放在前面,同样的方法会优先调用分类的内的方法

Category加载过程

我试图追中源码,但是根据下面的过程,没有全部追踪到。可能是版本的问题。不过思路都是一样的,仅供参考


image.png

Category合并到类中的过程:
运行时会通过remethodizeClass(class *cls)和 remethodizeClass(cls->ISA)方法将分类的信息分别合并到class对象和meta-class对象中。
合并的步骤:
1、类的分类按照编译顺序会存储在一个数组中category_list [_category_t,_category_t]
2、倒序遍历分类的数组(这也是最后编译的分类生效的原因)。并将分类的方法列表,属性列表,协议列表存储在对应的二维数组中。

 // 二维数组
 method_list_t **mlists = ;              // 分类中的方法列表 
 property_list_t **proplists = ;         // 分类中的属性列表
 protocal_list_t **protlists = ;         // 分类中的协议列表
 i= category_list->count
 mcount = 0
 propcount = 0
 protcount = 0
while(i--) { // 倒叙遍历分类
     category = category_list->list[i]
    
    /*
        [
            [method_t,method_t],
            [method_t,method_t]
        ]
    */
     method_list_t *mlist = category->methods
     if(mlist) {
          mlists[mcount ++] = mlist
     }
     
     /*
        [
            [property_t,property_t],
            [property_t,property_t]
        ]
    */
      property_list_t *proplist = category->porperities
     if(proplist) {
          proplists[propcount ++] = mlist
     }
     
     /*
        [
            [protocol_t,protocol_t],
            [protocol_t,protocol_t]
        ]
    */
     protocol_list_t *protlist = category->protocls
     if(protlist) {
          protlists[protcount ++] = mlist
     }
}

3、将方法列表协议列表属性列表合并到class对象或meta-class对象中

rw->methods.attchLists(mlist,mcount);
rw->properties.attchLists(proplist,propcount);
rw->protocol.attchLists(protlist,protcount);

4、attchLists过程:
将原来的对象方法指针向后移动分类的个数的位置,在把空出的位置指向新的分类的方法列表。因为分类是倒序遍历,并将方法取出来存储在二维数组中的,所以最后编译的分裂的方法会生效。


image.png

所以同样的方法会优先调用分类的内的方法

分类方法为什么会覆盖原来的方法?分类方法是真的覆盖原来的方法吗?

会是原来的方法不生效。不是真正的覆盖,因为先找到了分类中的方法,不会再往后找而已。

Category对象方法合并到Class对象方法列表中,Category的类方法合并到meta-class的类方法列表中

Category与Extension的区别是什么

分类实现原理:
底层结构是 _category_t结构体,里面存储了分类的对象方法,类方法,属性信息,协议信息,在程序运行的时候,runtime会将Category里的数据合并到类信息中(类对象、元类对象中)
Extension实现原理
扩展在编译器的时候就已经合并到类里了

区别:
扩展在编译的时候数据就已经包含在类信息中,category在运行期才会将数据合并到类信息中。

Category存在load方法吗

相关文章

  • 聊一聊Category

    Category的实现原理 Category中对象方法,在程序运行过程中,都会进入类的对象方法列表中。实例变量都是...

  • 聊一聊 Objective-C 的优缺点

    我们来聊一聊Objective-C的优缺点 总览 优点: 1.category2.运行时3.消息机制4.可以和C,...

  • 聊一聊

    就是这样,喜欢自我欺骗,明知道,真心想你,或者有事的人,会打电话给你。却还是忍不住的用微信,看一个人的消息和动态,...

  • 聊一聊

    记录一下,现在是女儿的生日。2020.7.25星期六 生日快乐我的小天使 微淼商学院说过最经典的话是:有些做商学院...

  • 聊一聊

    早在三天前师兄就告知我们今天上午老师会和我们在实验室聊一聊。校园卡余额不足,时间紧张未吃早餐,早上慌忙收拾赶紧到实...

  • 聊一聊

    大家好,我是野生梅花鹿。 马上就12点了,我决定用几分钟的时间随便写点啥~ 首先呢,是反省。 这个月,其实懒惰了很...

  • 聊一聊

    很久没写了,聊聊最近发生的事,十月份开始了一段长板之旅,一开始担心害怕摔,因为通过挑战一个个动作,挺有趣的,当你为...

  • 聊一聊

    疫情这些年,常常听到的是,哪哪被封控了之类,作为天选打工人,一直在正常上班搬砖中。 直至上周五晚接...

  • 聊一聊自律

    很久以前就知道韩雪,当时只觉得她是气质很优雅,长得很美丽的女明星,没有什么特殊的感觉,应该就是花瓶而已。 她的才气...

  • 睡前聊一聊

    每天晚上睡觉前和小胤聊一聊,这是我目前最喜欢的事情了。听听他讲一讲幼儿园的所见所闻,觉得人生都充满了童趣。 昨天小...

网友评论

      本文标题:聊一聊Category

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