1. strongToWeakObjectsMapTable
NSMapTable 如果用 strongToWeak灼卢,也就是key是strong,value是weak的話萝映,當一個key的value小時,雖然NSMapTable已經(jīng)沒有對象了,但是key并不會被release涉茧,仍舊有一個奇奇怪怪的東西拿住他:
雖然map里面只有一個鍵值對了,但是卻還是持有了多個key哦疹娶,所以其實key不會自動釋放的伴栓。
于是這個問題就到了 NSMapTable 的實現(xiàn)原理,根據(jù)這篇文章我找到了源碼http://www.ziyingtech.com/yk4sck.html#nsmaptable-%E6%BA%90%E7%A0%81
然后自己開始看起來0.0 講真沒想到如此復雜...
+ (void) initialize
{
if (abstractClass == 0)
{
abstractClass = [NSMapTable class];
concreteClass = [NSConcreteMapTable class];
}
}
其實 NSMapTable
的實現(xiàn)都是走的 NSConcreteMapTable
雨饺,后者是前者的子類哈钳垮。先看init叭:
// NSMapTable
- (id) initWithKeyOptions: (NSPointerFunctionsOptions)keyOptions
valueOptions: (NSPointerFunctionsOptions)valueOptions
capacity: (NSUInteger)initialCapacity
{
NSPointerFunctions *k;
NSPointerFunctions *v;
id o;
k = [[NSPointerFunctions alloc] initWithOptions: keyOptions];
v = [[NSPointerFunctions alloc] initWithOptions: valueOptions];
o = [self initWithKeyPointerFunctions: k
valuePointerFunctions: v
capacity: initialCapacity];
[k release];
[v release];
return o;
}
其實最后調(diào)到的還是 NSConcreteMapTable
的,子類里干了什么呢额港?開始其實就是去配置那個weak還是strong的option饺窿,如果沒有需要創(chuàng)建default的,然后就做了一個分配空間的事情:
GSIMapInitWithZoneAndCapacity(self, zone, initialCapacity);
GS_STATIC_INLINE void
GSIMapInitWithZoneAndCapacity(GSIMapTable map, NSZone *zone, uintptr_t capacity)
{
map->zone = zone;
map->nodeCount = 0;
map->bucketCount = 0;
map->buckets = 0;
map->nodeChunks = 0;
map->freeNodes = 0;
map->chunkCount = 0;
map->increment = 300000; // choosen so the chunksize will be less than 4Mb
GSIMapRightSizeMap(map, capacity);
GSIMapMoreNodes(map, capacity);
}
可以看出來 map 有好多屬性啊移斩,來到聲明里面康康:
@interface NSConcreteMapTable : NSMapTable
{
@public
NSZone *zone;
size_t nodeCount; /* Number of used nodes in map. */
size_t bucketCount; /* Number of buckets in map. */
GSIMapBucket buckets; /* Array of buckets. */
GSIMapNode freeNodes; /* List of unused nodes. */
GSIMapNode *nodeChunks; /* Chunks of allocated memory. */
size_t chunkCount; /* Number of chunks in array. */
size_t increment; /* Amount to grow by. */
unsigned long version; /* For fast enumeration. */
BOOL legacy; /* old style callbacks? */
union {
struct {
PFInfo k;
PFInfo v;
} pf;
struct {
NSMapTableKeyCallBacks k;
NSMapTableValueCallBacks v;
} old;
}cb;
}
@end
真的是一個 map 為啥要這么復雜肚医,于是他有很多node和bucket,這倆分別是啥呢向瓷?
struct _GSIMapNode {
GSIMapNode nextInBucket; /* Linked list of bucket. */
GSIMapKey key;
#if GSI_MAP_HAS_VALUE
GSIMapVal value;
#endif
};
struct _GSIMapBucket {
uintptr_t nodeCount; /* Number of nodes in bucket. */
GSIMapNode firstNode; /* The linked list of nodes. */
};
大概就是bucket就像一個鏈表頭肠套,node就是里面的內(nèi)容,但是map持有了一個 bucket 的array猖任,為啥要array呢你稚?現(xiàn)在來看看 set 的時候做了什么:
- (void) setObject: (id)anObject forKey: (id)aKey
{
GSIMapNode node;
if (aKey == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@:] given nil argument",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
node = GSIMapNodeForKey(self, (GSIMapKey)aKey);
if (node)
{
if (GSI_MAP_READ_VALUE(self, &node->value).obj != anObject)
{
GSI_MAP_RELEASE_VAL(self, node->value);
GSI_MAP_WRITE_VAL(self, &node->value, (GSIMapVal)anObject);
GSI_MAP_RETAIN_VAL(self, node->value);
version++;
}
}
else
{
GSIMapAddPair(self, (GSIMapKey)aKey, (GSIMapVal)anObject);
version++;
}
}
首先如果它找不到 key 綁定的 node,就會創(chuàng)建一個新的刁赖,如果找得到,就會釋放之前 node 里面的 value长搀,然后寫入一個新的 value。那么它是如何找到 node 的呢源请?
首先找 node 需要先找到一個 bucket,然后再找 node 就很容易了巢钓,其實就是鏈表的遍歷。but 如何找到 bucket 有點神奇:
GS_STATIC_INLINE GSIMapNode
GSIMapNodeForKey(GSIMapTable map, GSIMapKey key)
{
GSIMapBucket bucket;
GSIMapNode node;
if (map->nodeCount == 0)
{
return 0;
}
bucket = GSIMapBucketForKey(map, key);
node = GSIMapNodeForKeyInBucket(map, bucket, key);
return node;
}
GS_STATIC_INLINE GSIMapBucket
GSIMapBucketForKey(GSIMapTable map, GSIMapKey key)
{
return GSIMapPickBucket(GSI_MAP_HASH(map, key),
map->buckets, map->bucketCount);
}
GS_STATIC_INLINE GSIMapBucket
GSIMapPickBucket(unsigned hash, GSIMapBucket buckets, uintptr_t bucketCount)
{
return buckets + hash % bucketCount;
}
拿 bucket 的方式一看就是 map->buckets
指向一個地址症汹,然后連續(xù)有幾個 bucket,當我們放一個key的時候背镇,先拿他的 hash 看看放到哪個 bucket泽裳。(一旦您把第一個元素的地址存儲在 p 中,您就可以使用 p破婆、(p+1)、*(p+2) 等來訪問數(shù)組元素)
那么這些bucket是咋創(chuàng)建的呢祷舀?
new_buckets = (GSIMapBucket)NSZoneCalloc(map->zone, size,
sizeof(GSIMapBucket_t));
if (new_buckets != 0)
{
GSIMapRemangleBuckets(map, map->buckets, map->bucketCount, new_buckets,
size);
if (map->buckets != 0)
{
NSZoneFree(map->zone, map->buckets);
}
map->buckets = new_buckets;
map->bucketCount = size;
果然是根據(jù)數(shù)量 alloc [count * bucket size] 的大小,然后把舊的 bucket array里面的東西拷貝到新的bucket array裳扯。
如果之前木有 node,要如何新加呢饰豺?
GS_STATIC_INLINE GSIMapNode
GSIMapAddPair(GSIMapTable map, GSIMapKey key, GSIMapVal value)
{
GSIMapNode node = map->freeNodes;
if (node == 0)
{
GSIMapMoreNodes(map, map->nodeCount < map->increment ? 0: map->increment);
node = map->freeNodes;
}
map->freeNodes = node->nextInBucket;
GSI_MAP_WRITE_KEY(map, &node->key, key);
GSI_MAP_RETAIN_KEY(map, node->key);
GSI_MAP_WRITE_VAL(map, &node->value, value);
GSI_MAP_RETAIN_VAL(map, node->value);
node->nextInBucket = 0;
GSIMapRightSizeMap(map, map->nodeCount);
GSIMapAddNodeToMap(map, node);
return node;
}
如果在freeNodes
里面找不到空node,就創(chuàng)建一個然后將 key 和 value 都設置上冤吨,設置的方式我們可以看到蒿柳,首先需要 GSI_MAP_WRITE_KEY
然后還需要 GSI_MAP_RETAIN_KEY
。這里就涉及到如何實現(xiàn)的 weak 指針啦漩蟆。
write和retain的宏真的是不太容易懂垒探,大概是醬紫的:
#define GSI_MAP_WRITE_KEY(M, addr, x) \
if (M->legacy) \
*(addr) = x;\
else\
(IS_WEAK_KEY(M) ? pointerFunctionsAssign(&M->cb.pf.k, (void**)addr, (x).obj) : (*(id*)(addr) = (x).obj));
#define GSI_MAP_RETAIN_KEY(M, X)\
(M->legacy ? M->cb.old.k.retain(M, X.ptr) \
: IS_WEAK_KEY(M) ? nil : pointerFunctionsAcquire(&M->cb.pf.k, &X.ptr, X.ptr))
write看起來就是把數(shù)據(jù)寫進去了,然后retain會增加引用計數(shù)爆安,讓對象不會被釋放,因為retain最后會調(diào)到這段:
static inline void pointerFunctionsAssign(PFInfo *PF, void **addr, void *value)
{
if (memoryType(PF->options, NSPointerFunctionsWeakMemory))
{
ARC_WEAK_WRITE(addr, value);
}
else if (memoryType(PF->options, NSPointerFunctionsZeroingWeakMemory))
{
WEAK_WRITE(addr, value);
}
else if (memoryType(PF->options, NSPointerFunctionsStrongMemory))
{
STRONG_WRITE(addr, value);
}
else
{
*addr = value;
}
}
# define STRONG_WRITE(addr, x) objc_storeStrong((id*)addr, (id)x)
objc_storeStrong
就是runtime來強引用的一個方式仔引,所以如果你用strong option去持有 key 或者 value扔仓,他會真的持有它,即使它配對的鍵值對已經(jīng)被weak釋放了咖耘,也不會導致strong的釋放翘簇。
因為其實key value都是作為 node 的一部分來存儲的,當weak option 導致對象釋放的時候儿倒,其實node不會自動清空版保,它里面甚至好像還有指針指向那一塊內(nèi)存,因為它不知道你的key或者value已經(jīng)沒了夫否,但是某些節(jié)點會去check這個節(jié)點能不能被清空回收放到 freeNodes 里面彻犁。
上面只是我的理解哈,這個 NSMapTable 實在是有一丟丟復雜一堆奇奇怪怪的宏凰慈,我也不沒能太明白為啥搞得如此復雜0.0 anyway可以看出來的是汞幢,如果strong持有,即使鍵值對沒了還是會持有的哦微谓,無論是 key 還是 value森篷。
2. 找到修改某個函數(shù)的commit
refer: https://segmentfault.com/a/1190000020099456
如果你知道要找的代碼具體寫的是什么输钩,或者知道某個特別的關(guān)鍵字,你就可以用它來搜索仲智。
git log -S "config.menu_items"
本例中會查找所有包含 config.menu_items 的提交