做题系列之5---category

题:
ClassA:

@interface ClassA : NSObject
@end

@implementation ClassA

@end

NSObject+cate:

@interface NSObject (cate)
@end

@implementation NSObject (cate)
- (void)test
{
    NSLog(@"test Category");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [ClassA test]; 
    }
    return 0;
}

警告?报错?正常运行的输出
老规矩,先看clang后的代码
首先,

// @interface NSObject (cate)
/* @end */

是的没错,我们import的头文件编译之后被干掉了。。。
所以无论category声明中添加了什么函数,都没卵用。全让编译器干掉了。

继续往下看

// @implementation NSObject (cate)
static void _I_NSObject_cate_test(NSObject * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_vb_2zmknph106j18bb6sbds_qlr0000gn_T_NSObject_cate_148908_mi_0);
}
// @end

static struct _category_t _OBJC_$_CATEGORY_NSObject_$_cate __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
 "NSObject",
 0, // &OBJC_CLASS_$_NSObject,
 (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_cate,
 0,
 0,
 0,
};

static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
 &_OBJC_$_CATEGORY_NSObject_$_cate,
};
  • category编译后的名字_category_t OBJC$CATEGORY_NSObject$_cate是按照规则生成的字符串,也就是说呢,category不能重名
  • 最下面的指针数组,保存的是category的指针,并把它存在了Data数据块中的__objc_catlist中。

再看一下category的定义

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;
};
  • name是class name,非category name
  • cls是扩展的类对象,运行时赋值
  • instanceProperties表示Category里所有的properties

请比对结构体声明和上面的OBJC$CATEGORY_NSObject$_cate,对号入座。

顺便再来看一下category的方法是如何被添加到方法列表的

for (EACH_HEADER) {
    category_t **catlist = 
        _getObjc2CategoryList(hi, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = catlist[i];
        Class cls = remapClass(cat->cls);

        BOOL classExists = NO;
        if (cat->instanceMethods ||  cat->protocols  
            ||  cat->instanceProperties) 
        {
            addUnattachedCategoryForClass(cat, cls, hi);
            if (cls->isRealized()) {
                remethodizeClass(cls);
                classExists = YES;
            }
        }

        if (cat->classMethods  ||  cat->protocols) 
        {
            addUnattachedCategoryForClass(cat, cls->ISA(), hi);
            if (cls->ISA()->isRealized()) {
                remethodizeClass(cls->ISA());
            }
        }
    }
}

上一期介绍为什么类方法写在元类中的时候,引用了这段代码,这里再引用一下。
这里首先调用_getObjc2CategoryList获取category_t**。_getObjc2CategoryList的实现如下:

GETSECT(_getObjc2CategoryList,        category_t *,    "__objc_catlist");

__objc_catlist就是之前写在Data数据块中的内容。
接下来调用remethodizeClass,在remethodizeClass中调用attachCategoryMethods。

static void 
attachCategoryMethods(Class cls, category_list *cats, bool flushCaches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();
    method_list_t **mlists = (method_list_t **)
        _malloc_internal(cats->count * sizeof(*mlists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int i = cats->count;
    BOOL fromBundle = NO;
    while (i--) {
        method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= cats->list[i].fromBundle;
        }
    }

    attachMethodLists(cls, mlists, mcount, NO, fromBundle, flushCaches);

    _free_internal(mlists);
}

这里将category_list取出后生成mlist放入到mlists中,注意是倒序放入哦!

到此,相信你已经对问题的答案有所了解了。(答案是输出 test Category)

你可能感兴趣的:(做题系列之5---category)