美文网首页基础
iOS 底层学习9 -- 分类关联对象原理

iOS 底层学习9 -- 分类关联对象原理

作者: 恋空K | 来源:发表于2020-07-29 17:21 被阅读0次

    @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策略。

    相关文章

      网友评论

        本文标题:iOS 底层学习9 -- 分类关联对象原理

        本文链接:https://www.haomeiwen.com/subject/nlbbfktx.html