2015-04-29 2 views
2

Когда я использую objc_setAssociatedObject, я знаю, следует ли использовать сохранение или назначение, но я не знаю, как выбирать между OBJC_ASSOCIATION_RETAIN и OBJC_ASSOCIATION_RETAIN_NONATOMIC. Когда следует использовать одно или другое?objc_setAssociatedObject сохранить атомный или неатомный

+0

Вероятно, это зависит от того, является ли проблема с безопасностью потока проблемой и является ли сам вызывающий объект как атомным, так и неатомным. Здесь есть большой ответ, в котором подробно описывается разница между этими двумя: http://stackoverflow.com/questions/588866/whats-the-difference-between-the-atomic-and-nonatomic-attributes –

ответ

0

Если значение, которое вы пытаетесь сохранить, будет использовать атрибут nonatomic, если это свойство, используйте OBJC_ASSOCIATION_RETAIN_NONATOMIC, в противном случае используйте OBJC_ASSOCIATION_RETAIN.

0

Существует мало причин использовать атомарность (OBJC_ASSOCIATION_RETAIN). В большинстве случаев безопасность потоков не может быть достигнута путем обеспечения безопасности потока данных. Это больший предмет.

Для свойств есть побочный эффект атомизма, который считывает значение autoreleased. Но ни один из них не документирован для связанных объектов, и это не полезно в ARC раз.

Так что мой совет: используйте OBJC_ASSOCIATION_RETAIN_NONATOMIC.

6

Резюме: Вы должны использовать OBJC_ASSOCIATION_RETAIN, если вы могли бы назвать objc_setAssociatedObject на одну нить, и objc_getAssociatedObject в другом потоке, одновременно, с теми же object и key аргументов.

окровавленные детали:

Вы можете посмотреть на реализацию objc_setAssociatedObject в objc-references.mm. Но на самом деле разница между 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_RETAINOBJC_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, он сохранит и autorelease связанного объекта перед возвращением его к вам. Но что-то еще происходит, это не очевидно. Обратите внимание, что функция создает локальный экземпляр объекта 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, чтобы предотвратить состояние гонки, или вы можете обеспечить каким-то другим способом, чтобы вы никогда не устанавливали связь в одном потоке, а получая его на другом.

+0

Я столкнулся с этим точным состоянием гонки и ваше подробное объяснение помогло мне исправить, поэтому я вам благодарен! – khandpur

Смежные вопросы