[英]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.