簡體   English   中英

objc_setAssociatedObject保留原子或非原子

[英]objc_setAssociatedObject retain atomic or nonatomic

當我使用objc_setAssociatedObject ,我知道是使用retain還是assign,但我不知道如何在OBJC_ASSOCIATION_RETAINOBJC_ASSOCIATION_RETAIN_NONATOMIC之間OBJC_ASSOCIATION_RETAIN決定。 何時應該使用其中一種?

內容提要:必須使用OBJC_ASSOCIATION_RETAIN如果你可以稱之為objc_setAssociatedObject一個線程, objc_getAssociatedObject在另一個線程,同時,以相同的objectkey參數。

血腥細節:

您可以在objc_setAssociatedObjectobjc-references.mm的實現。 但實際上OBJC_ASSOCIATION_RETAINOBJC_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. */
};

請注意, 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)
}; 

所以我們可以看到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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM