【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性

如何使用关联对象给分类实现一个weak的属性

通过关联对象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修饰对象属性:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN = 0, //assignOBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //strong, nonatomicOBJC_ASSOCIATION_COPY_NONATOMIC = 3, //copy, nonatomicOBJC_ASSOCIATION_RETAIN = 01401, //strong, atomicOBJC_ASSOCIATION_COPY = 01403 //copy, atomic
};

思考:能否用assign实现?

weak和assign的区别如下:

  • **assign:**对应的所有权类型为__unsafe_unretained,当修饰对象的时候,修饰的指针指向该对象,不会去持有该对象,也不会使retainCount +1,而在指向的对象被释放时,依然指向原来的对象地址,不会被自动置为nil,所以造成了野指针,是不安全的;
  • **weak:**弱引用,不会影响对象的释放,而当对象被释放时(引用计数为0),所有指向它的弱引用都会自定被置为nil,防止野指针,之后再向该对象发消息也不会崩溃,weak是安全的;

看以下测试代码,使用policy为OBJC_ASSOCIATION_ASSIGN的策略,会发生什么样的情况?

//定义Person类
@interface Person : NSObject
@end
@implementation Person
- (void)dealloc {NSLog(@"Person dealloc");
}
@end@interface Person (Test)
//在分类中声明UIViewController属性,用assign修饰
@property(assign, nonatomic) UIViewController *viewController;
@end@implementation Person (Test)
- (void)setViewController:(UIViewController *)viewController {//利用objc_setAssociatedObject设置值,policy为OBJC_ASSOCIATION_ASSIGNobjc_setAssociatedObject(self, @selector(viewController), viewController, OBJC_ASSOCIATION_ASSIGN);
}
- (UIViewController *)viewController {//取值return objc_getAssociatedObject(self, _cmd);
}
@end

img

使用assign修饰对象,当离开作用域后,产生野指针访问Crash(如图),如何避免这个问题?

1、通过中间对象的方式

1.1、利用OBJC_ASSOCIATION_RETAIN_NONATOMIC + weak来实现;

创建中间类:

@interface WeakObjWrapper : NSObject
@property(weak, nonatomic) id weakObj;
@end
@implementation WeakObjWrapper
- (instancetype)initWithWeakObject:(id)weakObj {if (self = [super init]) {_weakObj = weakObj;}return self;
}
@end

实现属性的setter和getter:

@interface Person (Test)
@property(weak, nonatomic) UIViewController *viewController;
@end
@implementation Person (Test)
- (void)setViewController:(UIViewController *)viewController {WeakObjWrapper *warpper = objc_getAssociatedObject(self, @selector(viewController));//用warpper保存传递进来的值if (!warpper) {//warpper不存在则创建warpper = [[WeakObjWrapper alloc] initWithWeakObject:viewController];}else {//已经存在直接赋值warpper.weakObj = viewController;}//保存的实际上是warpper对象objc_setAssociatedObject(self, @selector(viewController), warpper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIViewController *)viewController {//获取到warpperWeakObjWrapper *warpper = objc_getAssociatedObject(self, _cmd);//取出warpper中的值return warpper.weakObj;
}
@end

objc_setAssociatedObject实际上存储的是WeakObjWrapper对象,对WeakObjWrapper对象产生强引用,WeakObjWrapper对象内部弱持有传递进去的值,保证在对象释放的时候,自动把值设置为nil,避免了崩溃;

1.2、借助OBJC_ASSOCIATION_COPY_NONATOMIC和弱引用block

-(void)setWeakvalue:(NSObject *)weakvalue {__weak typeof(weakvalue) weakObj = weakvalue;typeof(weakvalue) (^block)() = ^(){return weakObj;};objc_setAssociatedObject(self, weakValueKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSObject *)weakvalue {id (^block)() = objc_getAssociatedObject(self, weakValueKey);return block();
}

2、借助runtime

继续使用OBJC_ASSOCIATION_ASSIGN,利用runtime动态的创建传进去值的类的子类,在子类的dealloc中,将objc_setAssociatedObject中的value设置为nil,销毁并移除了关联对象,避免Crash,具体实现如下(具体使用已在注释中说明):

void weak_setAssociatedObject(id _Nonnull object,const void * _Nonnull key,id _Nullable value) {//派生一个子类,类名为WeakObjWrapper+value对应的类名const char *clsName = [[NSString stringWithFormat:@"WeakObjWrapper%@", [value class]] UTF8String];//获取派生的子类Class childCls = objc_getClass(clsName);//如果子类不存在,利用runtime动态的创建一个子类if (!childCls) {childCls = objc_allocateClassPair([value class], clsName, 0);objc_registerClassPair(childCls);}
    //注册dealloc方法SELSEL sel = sel_registerName("dealloc");//获取dealloc对应的类型编码const char *deallocEncoding = method_getTypeEncoding(class_getInstanceMethod([value class], sel));// 注意:内部持有value此处需要弱引用处理一下__weak typeof(value) weakValue = value;// 创建一个指向在调用dealloc方法时调用指定block的函数指针IMP deallocImp = imp_implementationWithBlock(^(id _childCls) {//在子类的dealloc方法中将value设置为nil,避免崩溃objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_ASSIGN);//派生的子类的dealloc方法会被调用,父类的不再被调用,故在此处调用一下父类的((void (*)(id, SEL))(void *)objc_msgSend)(weakValue, sel);});
    //给子类添加dealloc方法class_addMethod(childCls, sel, deallocImp, deallocEncoding);//将value对应的isa指向子类object_setClass(value, childCls);//设置关联对象objc_setAssociatedObject(object, key, value, OBJC_ASSOCIATION_ASSIGN);
}

注意:在派生的子类,添加的实现dealloc的方法中,重新调用一下父类的dealloc保证原有的类的释放关系不被破坏;调用(实现属性的getter和setter):

@interface Person (Test)
@property(assign, nonatomic) UIViewController *viewController;
@end
@implementation Person (Test)
- (void)setViewController:(UIViewController *)viewController {weak_setAssociatedObject(self, @selector(viewController), viewController);
}
- (UIViewController *)viewController {return objc_getAssociatedObject(self, _cmd);
}
@end

总结

关联对象中如何实现weak属性?

  • 关联对象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修饰对象属性;
  • 可以借助中间类(OBJC_ASSOCIATION_RETAIN_NONATOMIC + weak)来实现;
  • 也可以继续使用OBJC_ASSOCIATION_ASSIGN,利用runtime动态的创建传进去值的类的子类,在子类的dealloc中,将objc_setAssociatedObject中的value设置为nil,销毁并移除了关联对象,从而避免了Crash;

参考1:https://www.cnblogs.com/huangzs/p/14479408.html

参考2:https://developer.aliyun.com/article/1321927#:~:text=1%20关联对象objc_setAssociatedObject中的策略policy可知,并不支持使用weak修饰对象属性;%202%20可以借助中间类(OBJC_ASSOCIATION_RETAIN_NONATOMIC,%2B%20weak)来实现;%203%20也可以继续使用OBJC_ASSOCIATION_ASSIGN,利用runtime动态的创建传进去值的类的子类,在子类的dealloc中,将objc_setAssociatedObject中的value设置为nil,销毁并移除了关联对象,从而避免了Crash;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/676765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android:Volley框架使用

3.15 Volley框架使用 Volley框架主要作为网络请求,图片加载工具。当应用数据量小、网络请求频繁,可以使用Volley框架。 框架Github地址:https://github.com/google/volley Volley框架的简单使用,创建项目Pro_VolleyDemo。将Github上下载Volley框架源代码,volley-master.zi…

Linux中ps/kill/execl的使用

ps命令: ps -aus或者ps -ajx或者 ps -ef可以查看有哪些进程。加上 | grep "xxx" 可以查看名为”xxx"的进程。 ps -aus | grep "xxx" kill命令: kill -9 pid 杀死某个进程 kill -l 查看系统有哪些信号 execl函数&#…

JavaScript滚动事件

🧑‍🎓 个人主页:《爱蹦跶的大A阿》 🔥当前正在更新专栏:《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 滚动是网页交互不可或缺的一部分。监听页面和元素的滚动事件,可以帮助…

跟着cherno手搓游戏引擎【22】CameraController、Resize

前置: YOTO.h: #pragma once//用于YOTO APP#include "YOTO/Application.h" #include"YOTO/Layer.h" #include "YOTO/Log.h"#include"YOTO/Core/Timestep.h"#include"YOTO/Input.h" #include"YOTO/KeyCod…

力扣刷题之旅:进阶篇(五)—— 动态规划(DP)的妙用

力扣(LeetCode)是一个在线编程平台,主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目,以及它们的解题代码。 --点击进入刷题地址 引言: 在算法的世界中,动态规划&#xff…

[HTTP协议]应用层的HTTP 协议介绍

目录 1.前言 2.使用fiddler抓包来观察HTTP协议格式 3.HTTP协议的基本格式 2.1请求 2,1.1首行 2.1.2请求头 2.1.3空行 2.2响应 2.2.1首行 2.2.2响应头 键值对 ​编辑2.2.3空行 2.2.4载荷(响应正文) 3.认识URL 3.1关于URL encode 1.前言 我们在前面的博客中,简单的…

力扣231. 2 的幂(数学,二分查找,位运算)

Problem: 231. 2 的幂 文章目录 题目描述思路即解法复杂度Code 题目描述 思路即解法 思路1:位运算 1.易验证2的幂为正数; 2.易得2的幂用二进制表示只能有一个位为数字1 3.即将其转换为二进制统计其二进制1的个数 思路2:数学 当给定数n大于1时…

DNS 域名系统——应用层

目录 1 域名系统 DNS 1.1 域名系统 1.2 互联网的域名结构 1.2.1 顶级域名 TLD(Top Level Domain) (1) 国家顶级域名 nTLD (2) 通用顶级域名 gTLD (3) 基础结构域名 (infrastructure domain) 1.3 域名服务器 1.3.1 域名服务器的四种类型 (1…

Springboot拦截器中跨域失效的问题、同一个接口传入参数不同,一个成功,一个有跨域问题、拦截器和@CrossOrigin和@Controller

Springboot拦截器中跨域失效的问题 一、概述 1、具体场景 起因: 同一个接口,传入不同参数进行值的修改时,一个成功,另一个竟然失败,而且是跨域问题拦截器内的request参数调用getHeader方法时,获取不到前端…

JAVA设计模式之代理模式详解

代理模式 1 代理模式介绍 在软件开发中,由于一些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称为"代理"的第三者来实现间接访问.该方案对应的设计模式被称为代理模式. 代理模式(Proxy Design Pattern ) 原始定义是:让你能够提供对象的替代…

代码随想录算法训练营29期|day41 任务以及具体任务

第九章 动态规划part03 343. 整数拆分 class Solution {public int integerBreak(int n) {//dp[i] 为正整数 i 拆分后的结果的最大乘积int[] dp new int[n1];dp[2] 1;for(int i 3; i < n; i) {for(int j 1; j < i-j; j) {// 这里的 j 其实最大值为 i-j,再大只不过是重…

单片机学习笔记---DS1302时钟

上一节我们讲了DS1302的工作原理&#xff0c;这一节我们开始代码演示。 新创建一个工程写上框架 我们需要LCD1602进行显示&#xff0c;所以我们要将LCD1602调试工具那一节的LCD1602的模块化代码给添加进来 然后我们开始创建一个DS1302.c和DS1302.h 根据原理图&#xff0c;为了…

Java项目:19 基于SpringBoot的医院管理系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 医院管理系统 分为三个角色 管理员、医生、病人 管理员的主要功能&#xff1a;系统管理、医生管理、患者管理、预约管理、病史管理、住院信息管理、管…

ARM PAC/BTI/MTE三剑客精讲与实战

一、PAC指针认证精讲与实战 思考 1、什么是栈溢出攻击&#xff1f;什么是代码重用攻击&#xff1f;区别与联系&#xff1f; 2、栈溢出攻击的软&硬件缓解技术有哪些&#xff1f;在TF-A&OPTEE上的应用&#xff1f; 3、什么是ROP攻击&#xff1f;对ROP攻击的缓解技术&…

深入解析Linux中HTTP代理的工作原理

亲爱的Linux探险家们&#xff0c;准备好一起探索HTTP代理背后的神秘面纱了吗&#xff1f;在这个数字世界里&#xff0c;HTTP代理就像是一个神秘的中间人&#xff0c;默默地在你和互联网之间穿梭&#xff0c;为你传递信息。那么&#xff0c;这个神秘的中间人到底是如何工作的呢&…

centos7的git使用方法

下载git yum install git git克隆 git clone https...(图片中复制的内容) git提交到远程仓库 git add filename git commit -m "提交日志" git push git首次使用要配置邮箱和用户名 查看提交日志 git log 查看当前提交状态 git status

数据结构第十四天(树的存储/双亲表示法)

目录 前言 概述 接口&#xff1a; 源码&#xff1a; 测试函数&#xff1a; 运行结果&#xff1a; 往期精彩内容 前言 孩子&#xff0c;一定要记得你的父母啊&#xff01;&#xff01;&#xff01; 哈哈&#xff0c;今天开始学习树结构中的双亲表示法&#xff0c;让孩…

[算法前沿]--061-生成式 AI 的发展方向,是 Chat 还是 Agent?

什么是AI Agent (LLM Agent) AI Agent 的定义 AI Agent是一种超越简单文本生成的人工智能系统。它使用大型语言模型&#xff08;LLM&#xff09;作为其核心计算引擎&#xff0c;使其能够进行对话、执行任务、推理并展现一定程度的自主性。简而言之&#xff0c;Agent是一个具有…

上线GPT应用的流程

上线一个应用是一个逐步迭代的过程&#xff0c;不断根据用户反馈和市场需求进行改进和优化。上线一个基于GPT的应用通常需要以下步骤&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.明确目标和用途…

在线JSON解析格式化工具

在线JSON解析格式化工具 - BTool在线工具软件&#xff0c;为开发者提供方便。JSON在线可视化工具:提供JSON视图,JSON格式化视图,JSON可视化,JSON美化,JSON美化视图,JSON在线美化,JSON结构化,JSON格式化,JSON中文Unicode等等。以清晰美观的结构化视图来展示json,可伸缩折叠展示,…