一,Category结构体
typedef struct category_t { const char *name; //类的名字 classref_t cls; //类 struct method_list_t *instanceMethods; //category中所有给类添加的实例方法的列表 struct method_list_t *classMethods; //category中所有添加的类方法的列表 struct protocol_list_t *protocols; //category实现的所有协议的列表 struct property_list_t *instanceProperties; //category中添加的所有属性 } category_t;
从Category的定义也可以看出Category的可为(可以添加实例方法,类方法,甚至可以实现协议,添加属性)和不可为(无法添加实例变量)。
为什么很多人都说Category不能添加属性呢?
实际上,Category实际上允许添加属性的,同样可以使用@property,但是不会生成_变量(带下划线的成员变量),也不会生成添加属性的getter和setter方法的实现,所以,尽管添加了属性,也无法使用点语法调用getter和setter方法
二,首先需要分清楚两个概念:属性和变量
成员变量是定义在{}号中的变量,如果变量的数据类型是一个类则称这个变量为实例变量。因为实例变量是成员变量的一种特殊情况,所以实例变量也是类内部使用的,无需与外部接触的变量,这个也就是所谓的类私有变量。而属性变量是用于与其他对象交互的变量。
三,使用runtime去实现Category为已有的类添加新的属性并生成getter和setter方法
实例:1
#import <Foundation/Foundation.h> @interface NSArray (MyCategory) //不会生成添加属性的getter和setter方法,必须我们手动生成 @property (nonatomic, copy) NSString *blog;
#import "NSArray+MyCategory.h" #import <objc/runtime.h> @implementation NSArray (MyCategory) // 定义关联的key static const char *key = "blog"; /** blog的getter方法 */ - (NSString *)blog { // 根据关联的key,获取关联的值。 return objc_getAssociatedObject(self, key); } /** blog的setter方法 */ - (void)setBlog:(NSString *)blog { // 第一个参数:给哪个对象添加关联 // 第二个参数:关联的key,通过这个key获取 // 第三个参数:关联的value // 第四个参数:关联的策略
objc_setAssociatedObject(self, key, blog, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end
实例2
#import <UIKit/UIKit.h> @interface UIViewController (Router) /** 参数字典 */ @property (nonatomic, strong) NSDictionary *paramDic; @end
#import "UIViewController+Router.h" #import <objc/runtime.h> static const void *ParamDicKey = &ParamDicKey; @implementation UIViewController (Router) - (void)setParamDic:(NSDictionary *)paramDic { objc_setAssociatedObject(self, ParamDicKey, paramDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSDictionary *)paramDic { return objc_getAssociatedObject(self, ParamDicKey); } @end