iOS-Developer-OCiOS - Developer - OC 进阶大全

Object-C中Category

2019-08-01  本文已影响14人  zwwuchn

什么是Category

Objective-C类别也叫分类,是一种不需要继承即可给类扩充一些方法的语法技术。

Category使用

@interface LPPerson : NSObject
-(void)test;
@end
@implementation LPPerson
- (void)test {
    NSLog(@"LPPerson--test");
}
@end

@interface LPPerson (Extension)
- (void)test1;
+ (void)test2;
- @end
- @implementation LPPerson (Extension)
- (void)test1 {
    NSLog(@"LPPerson (Extension)--test1");
}
+ (void)test2 {
    NSLog(@"LPPerson (Extension)--+test2");
}
@end

@interface LPPerson (Eat)
- (void)eat1;
+ (void)eat2;
@end
@implementation LPPerson (Eat)
- (void)eat1 {
    NSLog(@"LPPerson (Eat)--eat1");
}
+ (void)eat2 {
    NSLog(@"LPPerson (Eat)--+eat2");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        LPPerson *person = [[LPPerson alloc] init];
        
        [person test];
        
        [person test1];
        [person eat1];
        
        [LPPerson test2];
        [LPPerson eat2];
        
    }
    return 0;
}
打印:
// LPPerson--test1
// LPPerson (Extension)--test1
// LPPerson (Eat)--eat1
// LPPerson (Extension)--+test2
// LPPerson (Eat)--+eat2

Category真面目

我们来将OC代码转换为C/C++代码或者查看runtime开源代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件名 -o 输出的CPP文件
struct _category_t {
    const char *name; //1
    struct _class_t *cls; //2
    const struct _method_list_t *instance_methods; //3
    const struct _method_list_t *class_methods; //4
    const struct _protocol_list_t *protocols; //5
    const struct _prop_list_t *properties; //6
};
编译后生成的代码结构
static struct _category_t _OBJC_$_CATEGORY_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "LPPerson",
    0, // &OBJC_CLASS_$_LPPerson,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LPPerson_$_Extension,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_LPPerson_$_Extension,
    0,
    0,
};
图片.png
这个category所有的-方法
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_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"test1", "v16@0:8", (void *)_I_LPPerson_Extension_test1}}
};
这个category所有的+方法
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_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"test2", "v16@0:8", (void *)_C_LPPerson_Extension_test2}}
};
图片.png

最后所有的对象方法放到类对象中,类方法放在元类对象中

Category本质

图片.png
static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    // 方法数组
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    // 属性数组
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    // 协议数组
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    while (i--) {
        // 取出某个分类
        auto& entry = cats->list[i];
        // 拿到分类的对象方法、类方法列表(根据isMeta判断对应的方法)
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }
    // 通过rw得到类对象里面的数据类型
    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    // 将所有分类的对象方法,附加到类对象的方法列表中
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);
    // 将所有分类的属性,附加到类对象的属性列表中
    rw->properties.attachLists(proplists, propcount);
    free(proplists);
    // 将所有分类的协议,附加到类对象的协议列表中
    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}
重点看attachLists方法
void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;

    if (hasArray()) {
        // many lists -> many lists
        uint32_t oldCount = array()->count;
        // 原来方法列表数量 + 新增加方法列表数量
        uint32_t newCount = oldCount + addedCount;
        // 重新分配分类方法列表数组内存结构
        setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
        array()->count = newCount;
        // array()->lists: 原来的方法列表向右挪动addedCount个位置
        memmove(array()->lists + addedCount, array()->lists, 
                oldCount * sizeof(array()->lists[0]));
        // addedLists: 所有分类方法列表(数组)
        // 将新添加的方法列表,拷贝到原来的方法列表(所以会拷贝在原来方法列表的前面->最后参与编译先调用)
        memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0]));
    }
    else if (!list  &&  addedCount == 1) {
        // 0 lists -> 1 list
        list = addedLists[0];
    } 
    else {
        // 1 list -> many lists
        List* oldList = list;
        uint32_t oldCount = oldList ? 1 : 0;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)malloc(array_t::byteSize(newCount)));
        array()->count = newCount;
        if (oldList) array()->lists[addedCount] = oldList;
        memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0]));
    }
}

总结

上一篇下一篇

猜你喜欢

热点阅读