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

news/2024/7/20 21:51:39 标签: iOS

文章目录

  • 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.niftyadmin.cn/n/5448818.html

相关文章

解决长尾问题,BEV-CLIP:自动驾驶中复杂场景的多模态BEV检索方法

解决长尾问题&#xff0c;BEV-CLIP&#xff1a;自动驾驶中复杂场景的多模态BEV检索方法 理想汽车的工作&#xff0c;原文&#xff0c;BEV-CLIP: Multi-modal BEV Retrieval Methodology for Complex Scene in Autonomous Driving 链接&#xff1a;https://arxiv.org/pdf/2401.…

移植 Zephyr 到 Art-Pi

背景 ​ 最近工作中接触到了 Zephyr&#xff0c;不由觉得 Zephyr 是个很强大、全面、优秀的实时操作系统&#xff0c;但同时是有一定的上手难度的&#xff0c;其复杂的构建系统让小编倒吸一口凉气。为了深入研究并完全掌控 Zephyr&#xff0c;小编决定把它移植到手头的开发板上…

【Redis】Redis 介绍Redis 为什么这么快?Redis数据结构Redis 和Memcache区别 ?为何Redis单线程效率也高?

目录 Redis 介绍 Redis 为什么这么快&#xff1f; Redis数据结构 Redis 和Memcache区别 &#xff1f; 为何Redis单线程效率也高&#xff1f; Redis 介绍 Redis 是一个开源&#xff08;BSD 许可&#xff09;、基于内存、支持多种数据结构的存储系统&#xff0c;可以作为数据…

HarmonyOS实战开发-如何使用首选项能力实现一个简单示例。

介绍 本篇Codelab是基于HarmonyOS的首选项能力实现的一个简单示例。实现如下功能&#xff1a; 创建首选项数据文件。将用户输入的水果名称和数量&#xff0c;写入到首选项数据库。读取首选项数据库中的数据。删除首选项数据文件。 最终效果图如下&#xff1a; 相关概念 首选…

Godot 学习笔记(5):彻底的项目工程化,解决GodotProjectDir is null+工程化范例

文章目录 前言GodotProjectDir is null解决方法解决警告问题根本解决代码问题测试引用其实其它库的输出路径无所谓。 工程化范例环境命名规范Nuget项目结构架构代码ISceneModelIOC服务 测试GD_Extension 通用扩展TestUtils GD_ProgramTestServiceMainSceneModel Godot对应的脚本…

RabbitMQ的事务机制

想要保证发送者一定能把消息发送给RabbitMQ&#xff0c;一种是通过Confirm机制&#xff0c;另一种就是通过事务机制。 RabbitMQ的事务机制&#xff0c;允许生产者将一组操作打包成一个原子事务单元&#xff0c;要么全部执行成功&#xff0c;要么全部失败。事务提供了一种确保消…

牛客NC170 最长不含重复字符的子字符串【高频 中等 map、滑动窗口 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/48d2ff79b8564c40a50fa79f9d5fa9c7 思路 用一个hashmap记录每个字母的index如果这个字母已经在map里了说明已经有重复了这样就更新看这个字母上次出现的index需要注意的是这种情况&#xff1a;“bacbca”这里的a…

基于Google云原生工程师的kubernetes最佳实践(三)

目录 三、集群管理 利用node affinity、taint等机制管理node 通过pod affinity/anti-affinity机制将pod分配到合适的node Node分级管理 从Qos角度将Pod分级 用namespace隔离不同的环境和用户 配置RBAC权限控制 1. 遵循最小权限原则 2. 使用 Role 和 ClusterRole 分离权…