iOS tagged pointer
從 64bit 開始,蘋果引入了 tagged pointer 計數,用于優化 NSNumber , NSDate , NSString 等小對象的存儲,沒有這個數據之前,NSNumber 等對象需要動態分配內存,維護引用計數,NSNumber 指針存儲的是堆中NSNumber對象的地址值,而引入了這個計數之后,NSNumber 指針里面存儲的數據是 : tag + data ,也就是直接將數據存儲在指針中。這樣做特別節省空間。如果這個數據特別大,指針存儲不下這個數,那么會回復之前的方式,存儲在堆區,然后指針存放堆區的地址。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0 );
for (int i = 0 ; i < 10000; i ++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"%@",@"123sdfasfdas"];
});
}
上面會發生什么呢?答案是可能崩潰,因為我們是多線程同時方位name的set方法,那么的set方法好比
-(void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name copy];
}
}
好比這樣,多個線程有可能同時去 release,所以會崩潰
解決方案,可以加鎖,在name設置前后去加鎖。
如果改成這樣呢?
dispatch_queue_t queue = dispatch_get_global_queue(0, 0 );
for (int i = 0 ; i < 10000; i ++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"%@",@"123"];
});
}
就是把一個長字符串變成一個短的字符創,這時候發現,沒有崩潰。因為后一個指針為 tagged pointer 類型的,就不存在release的操作,值直接就存儲再來指針的里面,直接取值就可以了,所以就不會崩潰,他就不是一個OC對象。
NSString *str = [NSString stringWithFormat:@"123"];
NSString *str1 = [NSString stringWithFormat:@"aefasfasdfasfdasf"];
NSLog(@"%@ %@",[str class],[str1 class]);
我們來打印下兩個類
NSTaggedPointerString __NSCFString
可以看到 第一個類為 tagged pointer 類,也證明了我們的猜想。
其實源碼里面是有判斷的,當為mac 的時候 &1,當為ios的時候&(1<<63),所以 ios 最高有效位為1就為 tagged pointer 類型,mac 最低有效位為1就為tagged pointer