/** * Policies related to associative references. * These are options to objc_setAssociatedObject() */ typedefOBJC_ENUM(uintptr_t, objc_AssociationPolicy) { 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. */0x0301 OBJC_ASSOCIATION_COPY = 01403/**< Specifies that the associated object is copied. * The association is made atomically. */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) };
void _object_set_associative_reference(id object, constvoid *key, id value, uintptr_t policy) { // This code used to work when nil was passed for object and key. Some code // probably relies on that to not crash. Check and handle it explicitly. // rdar://problem/44094390 if (!object && !value) return;
if (object->getIsa()->forbidsAssociatedObjects()) _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
if (value) { //try_emplace会先查找,没找到才创建新的ObjectAssociationMap,创建时会根据需要扩容AssociationsHashMap。 auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{}); if (refs_result.second) { //指定的disguised,以前没有值,这里是新创建的。则做好标记isFirstAssociation。 /* it's the first association we make */ isFirstAssociation = true; }
/* establish or replace the association */ auto &refs = refs_result.first->second; //refs是ObjectAssociationMap哈希表 auto result = refs.try_emplace(key, std::move(association)); //try_emplace作用同上。 if (!result.second) { //指定的key有旧值,进行swap,这样association保存的就是旧值,后面就执行释放旧值的操作。 association.swap(result.first->second); //result.first->second是ObjcAssociation对象 } } else { auto refs_it = associations.find(disguised); if (refs_it != associations.end()) { auto &refs = refs_it->second; auto it = refs.find(key); if (it != refs.end()) { association.swap(it->second); refs.erase(it); if (refs.size() == 0) { //ObjectAssociationMap哈希表全空时,则将其从AssociationsHashMap中清除。 associations.erase(refs_it); //清除AssociationsHashMap的某个桶。erase里还会对AssociationsHashMap进行减容或扩容。
} } } } }
// Call setHasAssociatedObjects outside the lock, since this // will call the object's _noteAssociatedObjects method if it // has one, and this may trigger +initialize which might do // arbitrary stuff, including setting more associated objects. if (isFirstAssociation) object->setHasAssociatedObjects();
// release the old value (outside of the lock). association.releaseHeldValue(); }
inlinevoidacquireValue() { if (_value) { switch (_policy & 0xFF) { case OBJC_ASSOCIATION_SETTER_RETAIN: _value = objc_retain(_value); break; case OBJC_ASSOCIATION_SETTER_COPY: _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy)); break; } } }
// We cannot use a C++ static initializer to initialize certain globals because // libc calls us before our C++ initializers run. We also don't want a global // pointer to some globals because of the extra indirection. // // ExplicitInit / LazyInit wrap doing it the hard way. template <typename Type> classExplicitInit { alignas(Type) uint8_t _storage[sizeof(Type)];