Objective-C

iOS:Category

2019-12-11  本文已影响0人  码小菜

目录
一,作用
二,本质
三,同名方法调用优先级

一,作用

1,给系统类或第三方类添加属性

@interface UIView (Add)
@property (nonatomic, copy) NSString *name;
@end

@implementation UIView (Add)
- (NSString *)name {
    return objc_getAssociatedObject(self, @"name");
}
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end

2,给系统类或第三方类添加方法

@interface UIView (Add)
- (void)removeAllSubviews;
@end

@implementation UIView (Add)
- (void)removeAllSubviews {
    [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj removeFromSuperview];
    }];
}
@end

3,拆分自定义类的代码

@interface ApiRequest : NSObject
@end

@interface ApiRequest (Login)
@end

@interface ApiRequest (Home)
@end

@interface ApiRequest (My)
@end
二,本质
@interface Person (Add) <NSCopying>
@property (nonatomic, assign) NSInteger age;
- (void)eat;
+ (void)run;
@end

@implementation Person (Add)
- (void)eat {
    NSLog(@"eat");
}
+ (void)run {
    NSLog(@"run");
}
@end

将上述代码用clang转为C++代码,Person (Add)的底层代码如下,可以看到分类的本质是结构体

// 底层结构
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;         // 属性列表
};

// 用_category_t实例化Person (Add)
static struct _category_t _OBJC_$_CATEGORY_Person_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "Person",
    0, // &OBJC_CLASS_$_Person,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Add,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Add,
    (const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_Add,
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Add, 
};

// Person (Add)的实例方法列表
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_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"eat", "v16@0:8", (void *)_I_Person_Add_eat}}
};

// Person (Add)的类方法列表
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_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"run", "v16@0:8", (void *)_C_Person_Add_run}}
};

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

// Person (Add)的属性列表
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_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"age","Tq,N"}}
};

注意:因为_category_t中没有成员变量列表,所以在分类中不能添加成员变量

三,同名方法调用优先级

1, 代码验证

编译顺序
// Person
@interface Person : NSObject
- (void)eat;
@end

@implementation Person
- (void)eat {
    NSLog(@"Person eat");
}
@end

// Person (Add1)
@interface Person (Add1)
- (void)eat;
@end

@implementation Person (Add1)
- (void)eat {
    NSLog(@"Person (Add1) eat");
}
@end

// Person (Add2)
@interface Person (Add2)
- (void)eat;
@end

@implementation Person (Add2)
- (void)eat {
    NSLog(@"Person (Add2) eat");
}
@end

// 使用
Person *person = [Person new];
[person eat];

// 打印
Person (Add1) eat

2,源码分析(源码下载地址

static void remethodizeClass(Class cls)
{
    category_list *cats;
    bool isMeta;

    runtimeLock.assertLocked();

    isMeta = cls->isMetaClass();

    // Re-methodizing: check for more categories

    // 获取所有的分类
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
        if (PrintConnecting) {
            _objc_inform("CLASS: attaching categories to class '%s' %s", 
                         cls->nameForLogging(), isMeta ? "(meta)" : "");
        }
        // 方法二
        attachCategories(cls, cats, true /*flush caches*/);        
        free(cats);
    }
}
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];
        // 取出分类中的方法列表
        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;
        }
    }

    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);
}
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;
        // 将原类的方法列表移动到数组末位
        memmove(array()->lists + addedCount, array()->lists, 
                oldCount * sizeof(array()->lists[0]));
        // 将分类的方法列表数组复制到原类方法列表数组的首位
        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]));
    }
}
步骤一 步骤二 步骤三

3,注意点

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self logMethodNamesWithClass:[Person class]];
}

// 打印类对象或元类对象中的方法名
- (void)logMethodNamesWithClass:(Class)cls {
    unsigned int count;
    Method *methodList = class_copyMethodList(cls, &count);
    NSMutableString *methodNames = [NSMutableString string];
    for (int i = 0; i < count; i++) {
        Method method = methodList[i];
        SEL selector = method_getName(method);
        NSString *methodName = NSStringFromSelector(selector);
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    free(methodList);
    NSLog(@"%@---%@", cls, methodNames);
}

// 打印
Person---eat, eat, eat,
上一篇下一篇

猜你喜欢

热点阅读