咱們定義一個LGPerson
的類,繼承於NSObject
,添加LGPerson
的分類LGPerson+LGA
,在LGPerson+LGA
分類中添加屬性name
,實現其set
跟get
方法,代碼以下 代碼執行結果以下所示markdown
咱們從實現分類屬性的代碼方法中能夠發現set
跟get
方法纔是重點,而其中的objc_setAssociatedObject
跟objc_getAssociatedObject
纔是重中之重。ide
objc_setAssociatedObject
分析咱們從源碼中查找objc_setAssociatedObject
函數
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
SetAssocHook.get()(object, key, value, policy);
}
複製代碼
SetAssocHook.get()
至關於接口模式,對外暴露接口,提供給外部使用,咱們繼續查看SetAssocHook
從中咱們能夠看到SetAssocHook
調用了_base_objc_setAssociatedObject
函數,咱們再來查看_base_objc_setAssociatedObject
函數_base_objc_setAssociatedObject
函數中調用了_object_set_associative_reference
函數ui
_object_set_associative_reference
分析源碼this
void
_object_set_associative_reference(id object, const void *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));
//包裝對象
DisguisedPtr<objc_object> disguised{(objc_object *)object};
//包裝對象(policy,value)
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
//此處代碼摺疊起來
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
object->setHasAssociatedObjects();
}
/* establish or replace the association */
auto &refs = refs_result.first->second;
auto result = refs.try_emplace(key, std::move(association));
if (!result.second) {
association.swap(result.first->second);
}
} 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) {
associations.erase(refs_it);
}
}
}
}
}
// release the old value (outside of the lock).
association.releaseHeldValue();
}
複製代碼
object
轉成objc_object
對象 acquireValue
函數根據policy
給屬性添加的是copy
仍是retain
AssociationsManager
是個析構函數,能夠聲明多個不是惟一的AssociationsHashMap
是全局惟一
的哈希表
,咱們從上面能夠看出AssociationsHashMap
是經過_mapStorage.get()
得到,而_mapStorage
是經過static void init()
初始化獲得,因爲使用了static
,所以AssociationsHashMap
是全局惟一
associations
尚未值;此時打印associations
已經由數據了,很明顯是第181行的代碼緣由。try_emplace
函數從上圖中咱們能夠得出經過關鍵字key
以及桶子TheBucket
來查找是否已經在哈希表
中存在,若是存在則返回,不然就插入LookupBucketFor
函數template<typename LookupKeyT>
bool LookupBucketFor(const LookupKeyT &Val,
const BucketT *&FoundBucket) const {
const BucketT *BucketsPtr = getBuckets();
const unsigned NumBuckets = getNumBuckets();
if (NumBuckets == 0) {
FoundBucket = nullptr;
return false;
}
// FoundTombstone - Keep track of whether we find a tombstone while probing.
const BucketT *FoundTombstone = nullptr;
const KeyT EmptyKey = getEmptyKey();
const KeyT TombstoneKey = getTombstoneKey();
assert(!KeyInfoT::isEqual(Val, EmptyKey) &&
!KeyInfoT::isEqual(Val, TombstoneKey) &&
"Empty/Tombstone value shouldn't be inserted into map!");
unsigned BucketNo = getHashValue(Val) & (NumBuckets-1);
unsigned ProbeAmt = 1;
while (true) {
const BucketT *ThisBucket = BucketsPtr + BucketNo;
// Found Val's bucket? If so, return it.
if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) {
FoundBucket = ThisBucket;
return true;
}
// If we found an empty bucket, the key doesn't exist in the set.
// Insert it and return the default value.
if (LLVM_LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) {
// If we've already seen a tombstone while probing, fill it in instead
// of the empty bucket we eventually probed to.
FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket;
return false;
}
// If this is a tombstone, remember it. If Val ends up not in the map, we
// prefer to return it than something that would require more probing.
// Ditto for zero values.
if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
!FoundTombstone)
FoundTombstone = ThisBucket; // Remember the first tombstone found.
if (ValueInfoT::isPurgeable(ThisBucket->getSecond()) && !FoundTombstone)
FoundTombstone = ThisBucket;
// Otherwise, it's a hash collision or a tombstone, continue quadratic
// probing.
if (ProbeAmt > NumBuckets) {
FatalCorruptHashTables(BucketsPtr, NumBuckets);
}
BucketNo += ProbeAmt++;
BucketNo &= (NumBuckets-1);
}
}
複製代碼
整個方法就是TheBucket查找過程,這個過程和cache_t的方法查找類似,都是循環查找,找不到平移知道找到或者查完爲止spa
插入非空值 插入空值 3d
objc_getAssociatedObject
分析id
objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
複製代碼
objc_getAssociatedObject
函數執行了_object_get_associative_reference
code
_object_get_associative_reference
找到則返回當前的值orm