objc_setAssociatedObject保留原子或非原子

Jef*_*eff 3 objective-c objective-c-runtime ios

当我使用时objc_setAssociatedObject,我知道是使用保留还是分配,但我不知道如何在OBJC_ASSOCIATION_RETAIN和之间做出决定OBJC_ASSOCIATION_RETAIN_NONATOMIC.何时应该使用其中一种?

rob*_*off 10

内容提要:必须使用OBJC_ASSOCIATION_RETAIN,如果你可以称之为objc_setAssociatedObject一个线程,并objc_getAssociatedObject在另一个线程,同时,与同objectkey参数.

血腥细节:

你可以看一下objc_setAssociatedObjectin 的实现objc-references.mm.但实际上是区别OBJC_ASSOCIATION_RETAIN而且OBJC_ASSOCIATION_RETAIN_NONATOMIC只是重要的区别objc_getAssociatedObject.

以下是这些常量的定义<objc/runtime.h>:

enum {
    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. */
};
Run Code Online (Sandbox Code Playgroud)

请注意,014010x0301014030x0303.源代码进一步打破了这些:

enum { 
    OBJC_ASSOCIATION_SETTER_ASSIGN      = 0,
    OBJC_ASSOCIATION_SETTER_RETAIN      = 1,
    OBJC_ASSOCIATION_SETTER_COPY        = 3,            // NOTE:  both bits are set, so we can simply test 1 bit in releaseValue below.
    OBJC_ASSOCIATION_GETTER_READ        = (0 << 8), 
    OBJC_ASSOCIATION_GETTER_RETAIN      = (1 << 8), 
    OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
}; 
Run Code Online (Sandbox Code Playgroud)

所以我们可以看到这OBJC_ASSOCIATION_RETAIN_NONATOMIC只是OBJC_ASSOCIATION_SETTER_RETAIN,但OBJC_ASSOCIATION_RETAIN实际上是OBJC_ASSOCIATION_SETTER_RETAIN | OBJC_ASSOCIATION_GETTER_RETAIN | OBJC_ASSOCIATION_GETTER_AUTORELEASE.

在以下OBJC_ASSOCIATION_GETTER_*位置检查这些位_object_get_associative_reference:

id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        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()) {
                ObjcAssociation &entry = j->second;
                value = entry.value();
                policy = entry.policy();
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
    }
    return value;
}
Run Code Online (Sandbox Code Playgroud)

因此,如果您使用OBJC_ASSOCIATION_RETAIN,它将保留并自动释放关联的对象,然后再将其返回给您.但还有其他一些事情并不明显.请注意,该函数创建了一个本地实例 AssociationsManager(它是存储在堆栈中的C++对象).这是以下定义AssociationsManager:

class AssociationsManager {
    static spinlock_t _lock;
    static AssociationsHashMap *_map;               // associative references:  object pointer -> PtrPtrHashMap.
public:
    AssociationsManager()   { spinlock_lock(&_lock); }
    ~AssociationsManager()  { spinlock_unlock(&_lock); }

    AssociationsHashMap &associations() {
        if (_map == NULL)
            _map = new AssociationsHashMap();
        return *_map;
    }
};
Run Code Online (Sandbox Code Playgroud)

所以你可以看到,当函数创建它时AssociationsManager,它会获取一个锁,它一直存在,直到管理器被销毁.在函数保留关联对象发生破坏.

在为密钥设置新的关联对象时使用相同的锁.此锁定可防止竞争条件,其中一个线程正在获取关联对象,而另一个线程正在替换它并导致该对象被释放.

如果你不阻止竞争条件,那么有一天你的多线程应用程序将通过尝试访问一个解除分配的对象而崩溃(或更糟).您可以使用它OBJC_ASSOCIATION_RETAIN来防止竞争条件,或者您可以通过某种其他方式确保您从未在一个线程上设置关联而将其与另一个线程相关联.


Tim*_*sen 0

如果您尝试存储的值是nonatomic属性,则使用该属性OBJC_ASSOCIATION_RETAIN_NONATOMIC,否则使用OBJC_ASSOCIATION_RETAIN