背景
今晚因为跟一个技术负责人聊了一会天,然后提到了Category不能直接添加成员变量这个事情,并且得知了具体的细节,想想应该记一下了。
Category不能直接添加成员变量
Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。其定义是:
typedef struct objc_class *Class;
objc_class结构体的定义如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父类
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0
long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法缓存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表
#endif
} OBJC2_UNAVAILABLE;
在上面的objc_class结构体,Ivars是objc_ivar_list(成员变量列表)指针;methodLists是指向objc_method_list指针的指针;
在Runtime里面,objc_class结构体的大小是固定的,不能够往这个结构体里面添加数据,只能做修改的操作,所以Ivars指向的是一个固定的区域,只能修改成员变量的值,不能增加成员变量数量。而methodList是二维数组,可以修改:*methodLists的值来增加成员方法,虽然无法扩展methodList指向的内存区域,却可以改变内存区域的值,所以可以动态添加方法,不能添加成员变量。
Category动态添加成员属性?!
跟上面一样,找了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;
从定义中可见—> 可添加:实例方法,类方法,协议实现,添加属性; 不可添加:无法添加实例变量。那经常说的不能添加属性?事实上应该是可以添加属性的,同样使用了@property,但是没有为其生成setter和getter方法以及没有生成_xx的成员变量,所以即使声明了
属性也无法使用点语法去调用,这才是真正的原因。因为我们在Category里面设置的关联值,其实就是利用runtime为其手动添加setter和getter方法/objc_getAssociatedObject / objc_setAssociatedObject,让一个对象保持对另一个对象的引用,获取得到另一个对象