iOS底层之关联对象(一)

首先我们来回忆一个经典的面试题

Category能否添加成员变量?如果可以,如何给Category添加成员变量?

首先由之前的知识我们知道,Category在底层生成的如下

iOS底层之关联对象(一)

从上面的结构我们都能看出来,并没有属性的定义,所以并不能直接添加,到底如何呢?,OK,我们继续探索.

接下来我们就一步一步的探索:首先我先创建一个继承NSObject的GDPerson,在创建一个GDPerson+Test的分类]

如下:

iOS底层之关联对象(一)iOS底层之关联对象(一)

我们知道,在GDPerson中的age它是系统给我们生成了3个:成员变量;get方法;set方法,而Test分类中声明了一个height的属性,我们先看一下程序的运行:

iOS底层之关联对象(一)

直接崩溃了,原来分类Test中的height中只是帮我们声明了,并没有做具体的实现,这时候我们这么做

iOS底层之关联对象(一)

我们明明赋值的是20,结果并没有起什么作用,这时候我们想,肯定是分类里面的具体实现出了问题.根据对应的场景,我们能想到以下方案可以尝试:

方案一:设置全局变量赋值

方案如下    main.m文件还是这样:    

GDPerson*person = [[GDPersonalloc]init];      

 person.age=10;   

  person.height=20; 

 NSLog(@"age:%d,height:%d",person.age,person.height);

iOS底层之关联对象(一)

输出:age:10,height:20,咦,好像解决了问题,这种方案行吗?其实存在弊端,比如下面的操作

iOS底层之关联对象(一)

方案二:设置全局字典方案存储

因为想到一一对应,我们就想到了如下的方案,上一篇博客我们知道,初始化我们可以写在load上面,因为是要唯一,我就取了当前对象的地址作为key.

iOS底层之关联对象(一)

这种也是似乎解决我们的问题,输出了height也确实是20,那这样是不是解决了问题呢?似乎好像解决了,不过如果再添加一个变量,我们还要定义一个NSMutableDictionary,还要写很多的代码.而且细心的同时可能会发现其他的一些问题,比如内存泄漏(这个泄漏看你怎么看这个问题),因为一直持有这个NSMutableDictionary,当然还有个问题就是线程安全的问题,可能我们要加一些锁,多线程等的知识.这种方案是行,就是很麻烦.

接下来我们就引出今天的主角:关联对象

方案三:设置关联对象之const void *key

首先看一下我们用的runtime的关联对象的一些定义

iOS底层之关联对象(一)

关联对象是苹果iOS底层之关联对象(一)

因为是const void *key 我直接定义个const void *变量试试,于是有了下面的代码

iOS底层之关联对象(一)

咦这样似乎解决了问题,height确实是20了,这样对吗?细心的同学可能发现,这样还是存在弊端,因为你的GDKey并不是唯一的,因为我们这样定义,意味着这个值是null,如果多一个变量,就不唯一了(这个比较简单,大家自己验证)

方案四:设置关联对象之常量区key

既然是const void *key是唯一的,那我立刻想到下面的代码,我直接用@“key”,这个是不是也行?字符串也是对象,这样写的话,它是存在常量区,在任何位置它的指针地址都是唯一的,所以这种方案肯定能解决,代码如下

iOS底层之关联对象(一)

height 是20,没有任何问题,这个方案也是比较好的方案,唯一的一个很小的弊端就是@"height"有可能出现手误写错的情况,所以要细心.

总结:

以上就是我分析的几种情况解决,当然还有其他分类添加属性变量的方案,我认为用关联属性是最好的方案,写起来也是最简单,我也是推荐大家这么做.

当然我认为最好的const void *key写法我并没有列出来,等下一篇博客,我会列出我认为最好的方案(上面的方案四肯定是没有问题的哈),也会从源码的角度去探索这个关联属性内部到底是怎么操作的,如果你有更好的方案,请告诉我😄

接下来博客我会Category解决添加属性的最终方案和介绍底层知识.

如果觉得我写得对您有所帮助,请关注我,我会持续更新😄

版权声明:玥玥 发表于 2021-03-19 10:49:31。
转载请注明:iOS底层之关联对象(一) | 女黑客导航