@property 自动帮我们做的三件事
第一件事情:生成一个带下划线的成员变量
第二件事情:声明一个set方法,一个get方法
第三件事情:生成set方法和get方法的具体实现
分类中声明属性,系统只会帮我们做一件事件,就是声明set方法和get方法,就没了。
我们不能直接给分类添加成员变量,但是可以间接现实分类有成员变量的效果
通过全局字典给分类添加类似属性的效果有很多点缺陷:比如
1.会存在内存泄露问题
2.会存在线程安全问题
设置关联对象:(Associated -- 关联的)
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
id _Nonnull object -- 对象,也就是说给哪个对象添加关联对象。
id _Nullable value -- 就是要关联的值
objc_AssociationPolicy policy -- 关联策略 (Policy -- 策略)
nonatomic -- 非线程安全;
获取关联对象:
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
总的来说:可以理解为把value用key存起来,类似字典
上图中的写法还是存在一些问题的。
static const void *MJNameKey = &MJNameKey
只要给全局变量加上了static,意味着这个全局变量的作用域仅限于当前文件(也就是仅限于MJPerson+Test.m这个文件)
const void * _Nonnull key需要传地址值, int p , &p就是p的地址值
我们定义的key前面加const是为了匹配,而且key根本不需要赋值,我们要的不是key里面存储的值,要的是key的地址值,所以我们只要定义下这个key就好了。
@“name”这种直接写出来的字符串是放在数据常量区的,也就是说这个@“name”不管写上多少次其内存地址都是不变的。
@selector(name),不管写多少次,只要传进去的方法名字是一样的,@selector(name)的地址值就是一样的(底层@selector返回的是某个结构体的指针)
- (NSString *)name:(id)self _cmd:(SEL)_cmd
self和_cmd 是隐式参数 在- (NSString *)name { }中写的_cmd. _cmd == @selector(name)
_cmd代表你当前方法的@selector - (void)setName:(NString *)name { }中写的_cmd . _cmd == @selector(setName:)
关联对象,设置value的key跟取value的key必须保持一致
我们使用objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)给分类传进去的value值,不是当作成员变量那样赛到person对象里面去的。它是分开存储的。也就是加的这个value是不会影响我们原来类对象的结构的,也不会影响到我们person实例对象在内存中的结构。
上图person对象在内存中就只有一个isa,一个_age.也就是说通过objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)给person分类添加的类似于属性的值,不是存储在person类对象,也不是存储在perosn实例对象里面,而是存储在其他地方。
HashMap对应oc中的字典。
DISGUISE(object)可以理解为拿到实例对象的地址。
所以关联对象是runtime自己维护了一个全局的AssociationsManager(也就是 *_map)
如果key对应的value为nil,也就是key对应的value为空
key对应的键值对就会被移除
AssociationsMap中存的一对一对的 viod * ObjectAssociation
运行上图会出现坏内存访问,也可以理解野也指针访问。
也就是说tempPerson销毁之后,上图中的value还存有tempPerson的地址值,这个时候再去访问已经销毁的tempPerson对象,就会发生坏内存访问。这也就说明了value这个东西不是弱引用,也就是不是weak修饰的。如果它是弱引用weak的话,相当于我这个person对象销毁之后,会把value置为nil。但是value并没有置为nil,所以value没有达到弱引用的效果。
使用什么关联策略,就跟我们平时用什么关键词修饰属性一样的道理。
关联策略就相当于将来你要保存传进来的值使用什么策略,就好比平时我们写属性的时候,会写strong /copy/weak.那这样就决定了,将来你这个属性,以怎样的内存管理方式去管理它
字符串我们一般用copy
既然我们外面声明用copy,那么我们希望内部也来个copy策略。