友友们都知道,分类当中是不能添加property的,因为运行时系统不会自动完成@synthesize,所以需要使用runtime的关联对象形式完成property。也就是这一对函数
objectivec
/**
设置关联对象
param object : 添加属性的对象,通常填入self
param key : 关联对象的SEL
param value : 关联对象的值
param policy : 关联对象的内存管理修饰词
*/
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
/**
获取关联对象
param object : 添加了属性的对象,通常填入self
param key : 关联对象的SEL
return : 关联对象
*/
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
其中setAssociated方法中有一个类型为objc_AssociationPolicy的参数policy,是代表这个关联对象的修饰词。点进去看一下,是一个枚举,其中有五个值:
objectivec
OBJC_ASSOCIATION_ASSIGN
OBJC_ASSOCIATION_RETAIN_NONATOMIC
OBJC_ASSOCIATION_COPY_NONATOMIC
OBJC_ASSOCIATION_RETAIN
OBJC_ASSOCIATION_COPY
有copy,有retain,还有assign,看起来足够用了。但是没有我们经常使用的weak修饰。weak属性在对象被释放掉的时候,系统会查找sideTable表将对应的weak指针设置为nil。这样即使我们错误地向它发送消息也不会报错,同时也不会发生循环引用的情况。
那我们能用assign代替它吗?assign属性在对象被释放的时候不会将指针置nil,所以如果我们误用会访问野指针造成崩溃 。在关联对象中也是如此吗?
尝试一下。
objectivec
//h文件
//简单创建一个NSObject的分类,命名为Person
//为这个分类添加一个property
@property (nonatomic, weak) NSObject *work;
进入m文件中进行属性关联的具体设置
objectivec
-(void)setWork:(NSObject *)work{
objc_setAssociatedObject(self,@selector(work), work, OBJC_ASSOCIATION_ASSIGN);
}
-(NSObject *)work{
objc_getAssociatedObject(self, @selector(work));
}
完成之后我们为这个分类的属性赋值
objectivec
NSObject *person = [NSObject new];
//为weak属性赋值
{
NSObject * weakObj = [NSObject new];
person.work = weakObj;
NSLog(@"作用域里 - %@",person.work);
}
NSLog(@"作用域外 - %@",person.work);
打印结果如下:
作用域里 - <NSObject: 0x600002ebc060>
第二行打印报EXC_BAD_ACCESS错误
看来关联对象的assign修饰和属性一样,离开作用域之后对象销毁,assign指针成为野指针,所以我们还是要想办法完成weak修饰。
这里有几种方法,目前使用的先记录下来,有空将剩下的补全。
一、创建一个中间类。
为中间类添加一个weak修饰的属性。通过中间类对象持有weak属性-分类属性关联中间类对象的形式完成分类添加weak属性的功能。可能说的不太清楚,看代码就理解了。简单的很,封装成了两个方法。
objectivec
//首先创建一个用来持有weak属性的继承自NSObject的中间类
@interface ZYWeakProxyObject : NSObject
@property (nonatomic, weak) id weakObject; ///<弱引用的属性
@end
//需要在m文件有实现
@implementation ZYWeakProxyObject
@end
接着我们在分类中添加封装的两个方法
objectivec
//h文件
/**
设置weak属性
param key : property的SEL
param value : property的值
*/
-(void)setWeakPropertyWithKey:(SEL)key Value:(id)value;
/**
获取weak属性
param key : property的SEL
*/
-(id)getWeakPropertyWithKey:(SEL)key;
//m文件
//设置weak属性
-(void)setWeakPropertyWithKey:(SEL)key Value:(id)value{
//获取保存的关联对象
ZYWeakProxyObject *weakProxyObj = objc_getAssociatedObject(self, key);
//如果没有就创建一个
if (!weakProxyObj) {
weakProxyObj = [ZYWeakProxyObject new];
}
//为中间类的弱属性赋值
weakProxyObj.weakObject = value;
/**
保存。这里注意保存的是中间类对象,associationPolicy是retain。
让对象强引用中间类对象,中间类对象弱引用真正需要weak的对象。
这样对象释放之后,中间类持有的弱引用也会被置nil不会引发崩溃。
而对象强引用的中间类对象,也会在对象被dealloc的时候进入dispose将关联对象销毁,不会发生内存泄漏。
*/
objc_setAssociatedObject(self, key, weakProxyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//获取weak属性
-(id)getWeakPropertyWithKey:(SEL)key{
//获取保存的关联对象
ZYWeakProxyObject *weakProxyObj = objc_getAssociatedObject(self, key);
//返回它持有的weak对象
return weakProxyObj.weakObject;
}
调用的话也很简单,直接在setter/getter方法中调用对应方法传参就行。
再次运行测验一下,打印结果如下:
作用域里 - <NSObject: 0x600000d9c000>
作用域外 - (null)
二、使用OBJC_ASSOCIATION_ASSIGN 来实现弱引用,在释放事件里面再将其释放掉,进而实现weak功能。