关联对象 AssociatedObject

主要包含两个方面内容:

  • 使用关联对象为已经存在的类添加属性

  • 关联对象在底层OC中的实现

  • @property可以说是一个 Objective-C 编程中的“宏”,它有元编程的思想。

    `

    @interface Property : NSObject
    /** ProPerty */
    @property (nonatomic, strong)NSString * property;
    @end

    在声明属性时上述代码做了如下3件事:
    • 生成实例变量_property
    • 生成 getter方法- property
    • 生成 setter方法- setProperty:
    @implementation Property {
    NSString *_property;
    }

    • (NSString *)property {
      return _property;
      }

    • (void)setProperty:(NSString *)property {
      _property = property;
      }

    @end

    `

    以上代码都是编译器自动生成隐藏在内部的,那么为什么在分类中不能声明属性?
    我们知道,在 Objective-C 中可以通过 Category 给一个现有的类添加属性,但是却不能添加实例变量,这似乎成为了 Objective-C 的一个明显短板。然而值得庆幸的是,我们可以通过 Associated Objects 来弥补这一不足。

    相关函数

    与Associated Objects相关的函数主要有3个,我们可以在 runtime 源码的 runtime.h 文件中找到它们的声明:

    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); id objc_getAssociatedObject(id object, const void *key); void objc_removeAssociatedObjects(id object);

    这三个函数的命名对程序员非常友好,可以让我们一眼就看出函数的作用:
    objc_setAssociatedObject
    用于给对象添加关联对象,传入 nil
    则可以移除已有的关联对象;
    objc_getAssociatedObject
    用于获取关联对象;
    objc_removeAssociatedObjects
    用于移除一个对象的所有关联对象。

    :objc_removeAssociatedObjects
    函数我们一般是用不上的,因为这个函数会移除一个对象的所有关联对象,将该对象恢复成“原始”状态。这样做就很有可能把别人添加的关联对象也一并移除,这并不是我们所希望的。所以一般的做法是通过给 objc_setAssociatedObject
    函数传入 nil
    来移除某个已有的关联对象。

    key 值

    关于前两个函数中的 key
    值是我们需要重点关注的一个点,这个 key值必须保证是一个对象级别(为什么是对象级别?看完下面的章节你就会明白了)的唯一常量。一般来说,有以下三种推荐的 key 值:
    声明 static char kAssociatedObjectKey,使用 &kAssociatedObjectKey 作为 key值;
    声明 static void *kAssociatedObjectKey = &kAssociatedObjectKey;,使用 kAssociatedObjectKey
    作为 key 值;
    用 selector ,使用getter 方法的名称作为 key值。

    objc_setAssociatedObject

    我们可以在 objc-references.mm文件中找到 objc_setAssociatedObject 函数最终调用的函数:

    `
    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);
    }

    `

    参考资料链接:
    http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/

    你可能感兴趣的:(关联对象 AssociatedObject)