新创建一个命令行项目,创建ZJPerson类和ZJPerson(Study)分类
这样分类就算间接完成添加属性的功能,我们在main函数中使用一下
可以看到分类添加的属性使用效果和在类里直接添加的属性效果一样
我们打开源码,搜索出objc_setAssociatedObject(, 找到这个方法的源码
点击进入_object_set_associative_reference方法
上面这段源码怎么理解呢
大概意思就是有AssociationsManager这么一个类,它的内部维护了一个全局的字典AssociationsHashMap
AssociationsHashMap字典的key对应的是disguised(object),类似于当前对象的内存地址,而value存储的是ObjectAssociationMap字典
ObjectAssociationMap字典的key对应的是添加的属性的名字,value呢,则对应的是ObjcAssociation实例
ObjcAssociation实例则存储着添加属性的值和策略
结构如下图所示
ZJPerson在study分类里添加了一个属性bookName,在main函数中给person实例的bookName属性赋值了@"How to study",那么系统是怎么存储这个属性的值呢?
其存储结构如下
在例如,我们在main函数中创建两个person对象
则其存储结构如下
Category能否添加成员变量?如果可以,如何给Category添加成员变量?
1、分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。所以<原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过其它方式添加属性>
为什么在分类中声明属性时,运行不会出错呢?
既然分类不让添加属性,那为什么我写了@property仍然还以编译通过呢?
我们知道在一个类中用@property声明属性,编译器会自动帮我们生成_成员变量和setter/getter,但分类的指针结构体中,根本没有属性列表。所以在分类中用@property声明属性,既无法生成_成员变量也无法生成setter/getter。
因此结论是:我们可以用@property声明属性,编译和运行都会通过,只要不使用程序也不会崩溃。但如果调用了_成员变量和setter/getter方法,报错就 在所难免 了。
既然报错的根本原因是使用了系统没有生成的setter/getter方法,可不可以在手动添加setter/getter来避免崩溃,完成调用呢?
其实是可以的。由于OC是动态语言,方法真正的实现是通过runtime完成的,虽然系统不给我们生成setter/getter,但我们可以通过runtime手动添加setter/getter方法。那具体怎么实现呢?
按照这个思路,我们通过运行时手动添加这个方法。
#import <objc/runtime.h>
static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey" //定义一个key值
@implementation Programmer (Category)
//运行时实现setter方法
- (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY)
}
//运行时实现getter方法
- (NSString *)nameWithSetterGetter {
return objc_getAssociatedObject(self, &nameWithSetterGetterKey)
}
@end
OC中在分类中添加属性用属性关联技术来实现存取值的:
那么系统的属性关联功能是如何实现的呢?
通过objc的相关源码追踪可以查看到:
注意点:
附:设置属性关联的源码
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)