当我使用objc_setAssociatedObject
时,我知道是使用保留还是分配,但我不知道如何在OBJC_ASSOCIATION_RETAIN
和OBJC_ASSOCIATION_RETAIN_NONATOMIC
之间做出决定。什么时候应该使用?
答案 0 :(得分:10)
执行摘要:如果您可以在一个主题上调用OBJC_ASSOCIATION_RETAIN
,则必须使用objc_setAssociatedObject
,而在另一个主题上调用objc_getAssociatedObject
同时使用object
{ {1}}和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
来防止竞争条件,或者您可以通过某种其他方式确保在将一个线程放在另一个线程上时从不设置关联。
答案 1 :(得分:0)
如果您尝试存储的值会使用nonatomic
属性(如果它是属性),请使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
,否则请使用OBJC_ASSOCIATION_RETAIN
。
答案 2 :(得分:0)
使用原子性(OBJC_ASSOCIATION_RETAIN
)几乎没有理由。在大多数情况下,通过使数据访问线程安全无法实现线程安全性。这是一个更大的主题。
对于属性,存在读取值自动释放的原子性的副作用。但是,对于关联对象也没有记录,也没有在ARC时代有用。
所以我的建议是:使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
。