【OC】为category添加weak属性

友友们都知道,分类当中是不能添加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功能。

相关推荐
童真的烂漫34 分钟前
error: Sandbox: rsync.samba in Xcode project
ios
瑶风1 小时前
go语言并发编程2-runtime
开发语言·golang·xcode
依旧风轻4 小时前
精确计算应用的冷启动耗时
ios·swift·cold start
**K4 小时前
how to use Xcode
ide·macos·xcode
#摩斯先生17 小时前
iOS 真机打包,证书报错No signing certificate “iOS Distribution” found
ios
花生君17 小时前
如何使用Xcode查看iOS APP客户端日志
ios·cocoa·xcode
zhaocarbon19 小时前
SSZipArchive 解压后 中文文件名乱码问题
ios
KillerNoBlood19 小时前
Objective-C语法基础
ios·objective-c
依旧风轻1 天前
iOS项目怎样进行二进制重排
ios·binary·reordering