Runtime-Category

2021-03-11  本文已影响0人  磊Se

Category的基本使用

如下代码,可以在里面添加方法和遵守协议。当然也可以重写方法(苹果不建议),或添加属性(没成员变量的属性)
自己实现get、set方法如下使用objc_getAssociatedObject

@interface NSObject (Log)
// Category 中不能添加成员变量(这样写需要自己实现get&set方法)
@property (nonatomic, copy) NSString *name;

- (void)testLog;

@end

@implementation NSObject (Log)

- (void)testLog {
   NSLog(@"添加方法");
}

#pragma mark - Getter & Setter
const void *kNameKey; //可用这样关联属性的key
- (NSString *)name { //属性的get方法
   //从 AssociationsHashMap 取值,以 @selector(name) 作为 Key
   NSString *kname = objc_getAssociatedObject(self, _cmd);
   return kname;
}

- (void)setName:(NSString *)name { //属性的set方法
   //在 AssociationsHashMap 添加 key-value
   objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}

@end

Association 关联对象的原理

主要有3个方法

// 用于给对象添加关联对象,传入 nil 则可以移除已有的关联对象;
  objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)

// 用于获取关联对象                        
  objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

// 移除所有的关联对象
  objc_removeAssociatedObjects(id _Nonnull object)
关联策略

通过objc_AssociationPolicy参数给对象添加关联策略(即assign、strong、copy、weak、原子属性)

结论:

Category 与 Extension的区别

Category 的本质

Category分为编译时期运行时期两个阶段。objc-runtime-new.h文件中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;
    // Fields below this point are not always present on disk.
    // 类成员属性列表
    struct property_list_t *_classProperties;
    // 用于返回方法列表
    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }
    // 用于返回属性列表
    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

Category的实现原理

编译时期Category并不会把其内容编译进Class中,只有在运行时期Class加载完之后,才会添加到已加载在内存中的Class中

1、每一个分类编译之后生成一个struct category_t结构体。里面存储着分类的对象方法、类方法、属性、协议信息;
2、把所有Category的方法、属性、协议数据,合并到一个大数组中;后面参与编译的Category数据,会在数组的前面
3、将合并后的分类数据(方法、属性、协议),插入到类原来的数据的前面(对象方法合并到类中,类方法合并到元类中)

分类执行顺序

1、方法名和原有类方法名相同时,分类会覆盖原有类的方法。
2、如果不同的分类中有相同的方法,调用的方法由编译器targets->Build Phases->Complie Source里添加的文件顺序决定,从上到下编译。调用的方法为最后编译的方法。(如顺序为A、B、C 则调用C中的方法)

上一篇 下一篇

猜你喜欢

热点阅读