OC对象 - 关联对象(如何给分类添加成员变量)

文章目录

  • OC对象 - 关联对象(如何给分类添加成员变量)
    • 1. 基本使用
      • 1.1 提供的API
        • 1.1.1 添加关联对象
        • 1.1.2 获得关联对象
        • 1.1.3 移除所有关联对象
        • 1.1.3 修饰符
      • 1.2 使用方法
      • 1.2 Key的常见用法
        • 1.2.1 使用的`get`方法的`@selecor`作为key
        • 1.2.2 使用指针的地址作为key
        • 1.2.3 使用static字符作为key
        • 1.2.4 使用属性名作为key
      • 1.3 错误用法举例
    • 2. 实现原理
      • 2.1 核心对象
      • 2.2 核心对象之间的关联
        • 2.2.1 示意图
    • 扩展
      • 如果setValue传了nil
        • 源码
      • 没有 weak

OC对象 - 关联对象(如何给分类添加成员变量)

Category(分类)的底层结构中,没有成员变量(ivar),因此不能给分类添加成员变量;在分类里面声明的属性,只会生成 get/set 方法的声明,没有方法的实现

所以我们不能直接给分类添加成员变量,但是可以间接实现,那就是使用关联对象

1. 基本使用

1.1 提供的API

1.1.1 添加关联对象
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
1.1.2 获得关联对象
id objc_getAssociatedObject(id object, const void * key)
1.1.3 移除所有关联对象
void objc_removeAssociatedObjects(id object)
1.1.3 修饰符
objc_AssociationPolicy对应的修饰符
OBJC_ASSOCIATION_ASSIGNassign
OBJC_ASSOCIATION_RETAIN_NONATOMICstrong, nonatomic
OBJC_ASSOCIATION_COPY_NONATOMICcopy, nonatomic
OBJC_ASSOCIATION_RETAINstrong, atomic
OBJC_ASSOCIATION_COPYcopy, atomic

1.2 使用方法

  • 创建ZSXPerson类,以及它的分类ZSXPerson+Text
  • ZSXPerson+Text.h中声明想要添加的属性
  • ZSXPerson+Text.m中使用关联对象API实现 get/set 方法

ZSXPerson类:

@interface ZSXPerson : NSObject@end@implementation ZSXPerson@end

ZSXPerson+Test.h

@interface ZSXPerson (Test)@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int weight;@end

ZSXPerson+Test.m

#import "ZSXPerson+Test.h"
#import <objc/runtime.h>@implementation ZSXPerson (Test)- (void)setName:(NSString *)name {objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}- (NSString *)name {// 隐式参数: _cmd,相当于当前方法的@selecor。(还有 self 也是隐式参数)return objc_getAssociatedObject(self, _cmd);
}- (void)setWeight:(int)weight {objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_ASSIGN);
}- (int)weight {return [objc_getAssociatedObject(self, _cmd) intValue];
}@end
  • 初始化两个实例对象,给属性设置不同的值,然后打印属性值


可以看到我们像正常使用对象的属性一样,使用分类中创建的属性

1.2 Key的常见用法

我们上面的示例,使用的get方法的@selecor来作为Key,这种写法相对比较简洁

1.2.1 使用的get方法的@selecor作为key
  • objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  • objc_getAssociatedObject(obj, @selector(getter))
1.2.2 使用指针的地址作为key

static void *MyKey = &MyKey;

  • objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  • objc_getAssociatedObject(obj, MyKey)
1.2.3 使用static字符作为key

static char MyKey;

  • objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  • objc_getAssociatedObject(obj, &MyKey)
1.2.4 使用属性名作为key
  • objc_setAssociatedObject(obj, @“property”, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  • objc_getAssociatedObject(obj, @“property”);

1.3 错误用法举例

static void *MyKey;

  • objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  • objc_getAssociatedObject(obj, MyKey)

如上写法,没有给*MyKey,他相当于是NULL,这时候,如果再有另一个属性使用Keystatic void *OtherKey;*MyKey*OtherKey都是 NULL,这就相当于把多个属性都与NULLKey关联,明显就出问题了

2. 实现原理

学习OC对象本质时,我们知道对象实际被转化成struct ClassName_IMPL结构体,对象的成员变量的值保存在该结构体中,例如:

struct ZSXStuden_IMPL {struct NSObject_IMPL NSObject_IVARS;int weight
};

使用关联对象给分类添加的成员变量,他并不是保存在该结构体下,因为Category(分类)的底层结构中并没有ivar

他是存储在全局的统一的一个AssociationsManager

2.1 核心对象

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

2.2 核心对象之间的关联

关联对象是通过这四个核心对象共同完成的,他们之间的关系是这样的

  • AssociationsManager 里面是一个AssociationsHashMap 类型的成员 map
  • AssociationsHashMap看名字是一个HashMap,我们可以认为是 iOS 中的字典,这个字段的 key 是被添加关联对象的对象的地址value 是个ObjectAssociationMap
  • ObjectAssociationMap一样认为是个字典,key 是我们调用objc_setAssociatedObject时传入的 key,value 是ObjectAssociation
  • ObjectAssociation里面有两个成员,_policy是我们设置的修饰符(比如:OBJC_ASSOCIATION_ASSIGN),_value是我们传入的值value
2.2.1 示意图

扩展

如果setValue传了nil

如果这么操作person.name = nil,相当于person的name这个关联对象,就是擦除如下图的数据

源码

没有 weak

objc_AssociationPolicy的值并没有weak,这点我们在使用的时候需要注意

@oubijiexi

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

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

相关文章

100 天机器学习指南

100 天机器学习指南 除了机器学习专栏&#xff0c;我们打算出另外一期专栏&#xff0c;叫做100 天机器学习指南&#xff0c;目标是通过100天的深入持续学习&#xff0c;让我们没有机器学习经验的人&#xff0c;也可以从事简单的机器学习工作&#xff0c;为职业生涯寻找增长点&…

LeetCode每日一题——移除链表元素

移除链表元素OJ链接&#xff1a;203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 这与之前的移除元素的题目很相似&#xff0c;那么我们同样可以用类似的做法&#xff08;双指针&#xff09;进行解题。但是这是一个链表删除&a…

微信小程序全局数据共享

文章目录 安装MobX相关的包根目录创建store文件夹&#xff0c;添加store.js文件绑定到页面中绑定到组件 mobx-miniprogram和mobx-miniprogram-bindings实现全局数据共享 mobx-miniprogram用来创建Store实例对象 mobx-miniprogram-bindings用来把Store中的共享数据或方法&…

树状数组原理和代码

树状数组 求下标的对应 求i管着的下标的范围 方法&#xff1a;拆掉最右侧的1然后1 到你自己 query sum 1-i的和 拆掉最右侧的1 再把下一个数值吸收到sum 重复这个过程直到全变0为止 add 方法&#xff1a;加上最右侧的1 到上限为止 lowbit方法 单点增加范围查询模板 #inc…

no main manifest attribute, in xxx.jar

找不到主类&#xff0c;如果是maven 项目&#xff0c;在pom.xml 指定主类 <mainClass>com.example.demo.Demo2Application</mainClass>还是不行的话&#xff0c;把 <skip>true</skip>去掉

请简单介绍一下Shiro框架是什么?Shiro在Java安全领域的主要作用是什么?Shiro主要提供了哪些安全功能?

请简单介绍一下Shiro框架是什么&#xff1f; Shiro框架是一个强大且灵活的开源安全框架&#xff0c;为Java应用程序提供了全面的安全解决方案。它主要用于身份验证、授权、加密和会话管理等功能&#xff0c;可以轻松地集成到任何Java Web应用程序中&#xff0c;并提供了易于理解…

nodejs+vue高校师资管理系统python-flask-django-php

快速发展的社会中&#xff0c;人们的生活水平都在提高&#xff0c;生活节奏也在逐渐加快。为了节省时间和提高工作效率&#xff0c;越来越多的人选择利用互联网进行线上打理各种事务&#xff0c;然后线上管理系统也就相继涌现。与此同时&#xff0c;人们开始接受方便的生活方式…

从FasterTransformer源码解读开始了解大模型(1.0)了解FasterTransformer

从FasterTransformer源码解读开始了解大模型&#xff08;1.0&#xff09;了解FasterTransformer 写在前面的话 最近的一年时间真是令人感慨&#xff0c;换了个工作方向&#xff0c;学了些深度算子开发相关知识&#xff0c;工作也转到对LLM的学习和开发了。入行不算深&#xf…

【Node.js】markdown 转 html

markdown 转 html 需要先安装三个包&#xff1a;browser-sync&#xff0c;ejs&#xff0c;marked 第一步&#xff0c;先准备一个 ejs 模板&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title><% title %>…

谷粒商城——缓存的概念

1. 使用缓存的好处&#xff1a;减少数据库的访问频率&#xff0c;提高用户获取数据的速度。 2. 什么样的数据适合存储到缓存中&#xff1f; ①及时性、数据一致性要求不高的数据&#xff0c;例如物流信息、商品类目信息 ②访问量大更新频率不高的数据(读多、写少) 3. 读模式…

PyTorch中用torch.block_diag()将多个矩阵沿对角线拼接成一个大矩阵的函数

torch.block_diag()是PyTorch中用于将多个矩阵沿对角线拼接成一个大矩阵的函数。这个函数可以用于构建卷积神经网络中的卷积核矩阵&#xff0c;或者构建变分自编码器等需要对多个线性变换进行堆叠的模型。 torch.block_diag()函数的语法如下&#xff1a; torch.block_diag(*a…

加密技术概述

传输数据时的四个问题 窃听 数字加密 假冒 消息认证或数字签名 篡改 消息认证码或数字签名 事后否认 数字签名 加密技术 将数据变成第三者的计算机无法理解的形式&#xff0c;然后再将其恢复成原本数据的一系列操作就是加密技术。 哈希函数 哈希函数可以把给定的数据转…

C++中的string类

一、构造函数 1.默认构造函数&#xff08;defalut constructor&#xff09;&#xff1a;string() 2.指针拷贝构造函数&#xff08;from c-string&#xff09;&#xff1a;string(const char* s) 3.拷贝构造函数&#xff08;copy constructor&#xff09;&#xff1a;string(…

设计模式之状态模式(一)

设计模式专栏&#xff1a; http://t.csdnimg.cn/4Mt4u 目录 1.概述 2.结构 3.实现 4.总结 1.概述 状态模式( State Pattern)也称为状态机模式( State Machine pattern), 是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类, 属于行为型模式。 在状…

多轴机械臂运动控制:4x4坐标变换矩阵该用C语言的二维数组还是一维数组?

做多轴机械臂的运动控制&#xff0c;免不了要对4x4的坐标变换矩阵进行乘法&#xff0c;C语言中可以用二维数组或者一维数组来实现矩阵&#xff0c;下面来比较一下二维数组和一维数组的性能差异。 开发环境&#xff1a;Visual Studio 2022&#xff0c;分别在Debug和Rele…

Linux ~ 查看日志的常用命令总结

1.tail -n <行数>&#xff0c;显示文件的尾部n行内容。 -f 循环读取&#xff0c;常用于查阅正在改变的日志文件。 ① tail -f test.log 实时显示test.log文件里的最尾部的内容&#xff0c;只要test.log更新就可以看到最新的文件内容。 ② tail -100f test.log 实时监控…

IOS面试题编程机制 41-45

41. lldb(gdb)常用的控制台调试命令?1). p 输出基本类型。是打印命令,需要指定类型。是print的简写 p (int)[[[self view] subviews] count] 2). po 打印对象,会调用对象description方法。是print-object的简写 po [self view] 3). expr 可以在调试时动态执行指定表达式,…

Vue3+echarts绘制世界地图

先放效果图 之前所查找的资料都没有讲清楚如何引入地图文件并绘制地图&#xff0c;下面做一个记录。 首先下载对应的地图json文件&#xff0c;这里可以参考我的这篇文章&#xff0c;提供了下载地址&#xff1a;记录echarts各种地图json文件下载地址-CSDN博客 第二步&#xff…

笔记本和台式机主板内部结构分析

笔记本和态势机主板内存接口以及配件安装位置 笔记本主板 1 以thinkpad L-490为例,使用拆机小工具拆机&#xff0c;打开后面板&#xff0c;内部结构示意图如下 台式机主板 以技嘉-B660M-AORUS-PRO-AX型号主板为例 笔记本电脑和台式机电脑的相同之处 CPU&#xff1a;笔记本…

IOS面试题编程机制 36-40

36. 阐述IOS ViewController生命周期?1. initWithCoder:通过nib文件初始化时触发。 2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。 3. loadView:开始加载视图控制器自带的view。 4. viewDidLoad:视图控制器的view被加载完成…