[英]objc_setAssociatedObject retain atomic or nonatomic
当我使用objc_setAssociatedObject
,我知道是使用retain还是assign,但我不知道如何在OBJC_ASSOCIATION_RETAIN
和OBJC_ASSOCIATION_RETAIN_NONATOMIC
之间OBJC_ASSOCIATION_RETAIN
决定。 何时应该使用其中一种?
内容提要:必须使用OBJC_ASSOCIATION_RETAIN
如果你可以称之为objc_setAssociatedObject
一个线程, objc_getAssociatedObject
在另一个线程,同时,以相同的object
和key
参数。
血腥细节:
您可以在objc_setAssociatedObject
中objc-references.mm
的实现。 但实际上OBJC_ASSOCIATION_RETAIN
和OBJC_ASSOCIATION_RETAIN_NONATOMIC
之间的区别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_SETTER_RETAIN | OBJC_ASSOCIATION_GETTER_RETAIN | OBJC_ASSOCIATION_GETTER_AUTORELEASE
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
来防止竞争条件,或者您可以通过某种其他方式确保您从未在一个线程上设置关联,同时将其与另一个线程相关联。
如果您尝试存储的值将使用nonatomic
属性(如果它是属性),请使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
,否则使用OBJC_ASSOCIATION_RETAIN
。
使用原子性几乎没有理由( OBJC_ASSOCIATION_RETAIN
)。 在大多数情况下,通过使数据访问线程安全无法实现线程安全性。 这是一个更大的主题。
对于属性,存在原子性的副作用,即读取值是自动释放的。 但是,对于关联对象也没有记录,也没有在ARC时代有用。
所以我的建议是:使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.