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

相关推荐
悟空瞎说14 小时前
NSNotificationCenter(NotificationCenter)官方文档白话翻译 + API 详解 + Swift 实战
ios
末代iOS程序员华仔21 小时前
iOS个人开发者账号申请为啥会被卡住
ios·swift·figma
MaoJiu1 天前
从纹理采样到局部形变:实时瘦鼻 Shader 算法解析
ios
奈斯先生Vector1 天前
从“能调用”到“可替换”:Coze 多模型 API 编排层设计指南
linux·运维·人工智能·ubuntu·平面·ios
DogDaoDao1 天前
HarmonyOS 深度解析:从微内核到 ArkTS 工程实战
android·linux·ios·华为·程序员·harmonyos·harmonyos next
Patrick_Wilson2 天前
iOS Safari Clipboard API 权限弹窗问题
前端·ios·safari
_瑞2 天前
APM_符号化原理
ios·汇编语言
花椒技术2 天前
不是跑通一次 Demo:花椒 QA 怎样把真机 UI 自动化做成回归流程
ios·单元测试·测试
2501_915918412 天前
Swift IDE 有哪些?按平台和需求选择Swift 开发工具
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
HySpark2 天前
语音分段策略优化实践:如何解决错误分段引发的级联误差
ide·macos·语音识别·xcode·熙瑾会悟