【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功能。

相关推荐
开心就好20252 天前
iOS App 安全加固流程记录,代码、资源与安装包保护
后端·ios
开心就好20252 天前
iOS App 性能测试工具怎么选?使用克魔助手(Keymob)结合 Instruments 完成
后端·ios
zhongjiahao3 天前
面试常问的 RunLoop,到底在Loop什么?
ios
wvy4 天前
iOS 26手势返回到根页面时TabBar的动效问题
ios
RickeyBoy4 天前
iOS 图片取色完全指南:从像素格式到工程实践
ios
aiopencode4 天前
使用 Ipa Guard 命令行版本将 IPA 混淆接入自动化流程
后端·ios
二流小码农4 天前
鸿蒙开发:路由组件升级,支持页面一键创建
android·ios·harmonyos
iceiceiceice5 天前
iOS PDF阅读器段评实现:如何从 PDFSelection 精准还原一个自然段
前端·人工智能·ios
ssshooter7 天前
Tauri 踩坑 appLink 修改后闪退
前端·ios·rust
二流小码农7 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos