美文网首页iOS技术
iOS之AssociateObject(关联对象)

iOS之AssociateObject(关联对象)

作者: 环宇飞杨 | 来源:发表于2020-05-07 13:12 被阅读0次

简介

AssociateObject 一般用于对已有对象的参数扩展,可认为是一种比较偷懒的继承方式,搭配Category使用,基本可以等同于继承了

Category不支持ivar主要还是因为类在被注册之后就不可再对其变量进行增减,免得造成同一个类,功能意义却大不相同的情况。

AssociateObject采用了一套和类完全无关的管理方式,用于和类的实例彻底解耦,所以其内存管理,释放时机和实例都有所不同,以下稍作总结。

setter/getter

  1. objc_getAssociatedObject(self, key);
    getter非常简单,从全局map中以预先设置的key拿到对应对象。此处的key一般可写做_cmd,也就是当前getter方法,之前还需要写一个static char类型的key,使用时候还需要转换为指针,后来发现直接使用_cmd更好,还能保证key的唯一性。
  2. objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    setter的value需要是对象类型,基本类型需要使用字面量包装下,最后一个修饰符稍后再讲,先看下关联对象被储存在什么地方,并且是如何储存的。

存取原理

先介绍四个关键类

  • AssociatedManager
  • AssociatedHashMap
  • ObjcAssociationMap
  • ObjcAssociation
    从下往上依次介绍,顺便推理原理
  1. ObjcAssociation,对应和实例绑定的实际对象,内部为对象具体信息,值、类型、修饰符等等。
  2. ObjcAssociationMap,该map用于储存查找上面实际对象,此处的key猜测应该是上述两个存取方法中的key(_cmd),也很好理解
  3. AssociatedHashMap,该层的意义就是为全局的关联对象操作做准备了,系统下各个类都可以创建关联对象,所以建一个存放类和其对应的所有关联属性的容器就很有必要了,此处map的value为所有关联对象的数组,要注意的是key是什么?是当前类呢还是类的实例?(其实细想下就会知道,给UIView增加left属性,那么每个view对象的left都是不一样的,所以key应该为当前实例,也就是self)。
  4. AssociatedManager,全局单例无疑了,用于runtime下便捷调用AssociatedHashMap容器的存取方法唯一入口,另外还控制着AssociatedHashMap的生命周期。

下面摘抄下源码:

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
类图

内存管理

objc_setAssociatedObject方法接收的四个参数中,最后一个与引用计数相关

/**
 * Policies related to associative references.
 * These are options to objc_setAssociatedObject()
 */
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

基本和MRC下的属性修饰符无异,对照使用即可,一个注意点就是assign修饰下对象野指针的问题,一些面试题中会问如何实现weak类型的关联对象,就是需要使用OBJC_ASSOCIATION_ASSIGN,仿照weak原理,当对象释放时,将关联对象置空即可。那么关联对象是什么时候释放的呢?

释放时机

dealloc 方法的调用顺序是从子类到父类直至 NSObject 的,NSObject 的 dealloc 会调用 object_dispose() 函数,进而移除 Associated Object。

由此可见,传言中的关联对象释放时机比实例晚的多,可以在这句话中得到验证。

相关文章

  • iOS之AssociateObject(关联对象)

    简介 AssociateObject 一般用于对已有对象的参数扩展,可认为是一种比较偷懒的继承方式,搭配Categ...

  • iOS关联对象技术原理

    iOS关联对象技术原理 iOS关联对象技术原理

  • iOS底层原理总结 - 关联对象实现原理

    iOS底层原理总结 - 关联对象实现原理 iOS底层原理总结 - 关联对象实现原理

  • iOS之关联对象

    我们在 iOS 开发中经常需要使用分类(Category),为已经存在的类添加属性的需求,但是使用@propert...

  • iOS~runtime之关联对象

    什么是runtime? RunTime又叫运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消...

  • iOS底层之关联对象

    首先我们来简单的描述一下分类的一些基本概念:1、用来给类添加新方法2、不能给类添加成员属性,添加了成员变量,也无法...

  • iOS底层之关联对象

    首先我们来回忆一个经典的面试题 Category能否添加成员变量?如果可以,如何给Category添加成员变量? ...

  • iOS Objective-C 关联对象

    iOS Objective-C 关联对象 1. 关联对象简介 对于关联对象,我们熟悉它的地方就是给分类添加属性。虽...

  • iOS对象关联

    什么是关联对象 关联对象是指某个OC对象通过一个唯一的key连接到一个类的实例上。 举个例子:xiaoming是P...

  • iOS:关联对象

    目录一,添加属性二,基本知识三,底层原理四,注意点 一,添加属性 1,在类中添加属性,系统会自动生成带下划线的成员...

网友评论

    本文标题:iOS之AssociateObject(关联对象)

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