执行摘要:如果您可能在一个线程上调用objc_setAssociatedObject,并在另一个线程上同时调用objc_getAssociatedObject,则必须使用OBJC_ASSOCIATION_RETAIN,同时使用相同的object 和key论据。
血腥细节:
你可以在objc-references.mm中查看objc_setAssociatedObject的实现。但实际上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. */
};
请注意,01401 是 0x0301,01403 是 0x0303。源代码进一步细分:
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)
};
所以我们可以看到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;
}
因此,如果您使用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;
}
};
所以你可以看到,当函数创建它的AssociationsManager 时,它会获取一个锁,它会一直持有该锁直到管理器被销毁。销毁发生在函数保留了关联对象之后。
为键设置新的关联对象时使用相同的锁。此锁可防止出现竞争条件,即一个线程获取关联对象,而另一个线程替换它并导致对象被释放。
如果您不阻止竞态条件,那么某天您的多线程应用程序会因尝试访问已释放的对象而崩溃(或更糟)。您可以使用OBJC_ASSOCIATION_RETAIN 来防止竞争条件,或者您可以通过其他方式确保您永远不会在一个线程上设置关联,同时在另一个线程上设置关联。