iOS tagged pointer
從 64bit 開始,蘋果引入了 tagged pointer 計(jì)數(shù),用于優(yōu)化 NSNumber , NSDate , NSString 等小對(duì)象的存儲(chǔ),沒有這個(gè)數(shù)據(jù)之前,NSNumber 等對(duì)象需要?jiǎng)討B(tài)分配內(nèi)存,維護(hù)引用計(jì)數(shù),NSNumber 指針存儲(chǔ)的是堆中NSNumber對(duì)象的地址值,而引入了這個(gè)計(jì)數(shù)之后,NSNumber 指針里面存儲(chǔ)的數(shù)據(jù)是 : tag + data ,也就是直接將數(shù)據(jù)存儲(chǔ)在指針中。這樣做特別節(jié)省空間。如果這個(gè)數(shù)據(jù)特別大,指針存儲(chǔ)不下這個(gè)數(shù),那么會(huì)回復(fù)之前的方式,存儲(chǔ)在堆區(qū),然后指針存放堆區(qū)的地址。
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"];
});
}
上面會(huì)發(fā)生什么呢?答案是可能崩潰,因?yàn)槲覀兪嵌嗑€程同時(shí)方位name的set方法,那么的set方法好比
-(void)setName:(NSString *)name{
if (_name != name) {
[_name release];
_name = [name copy];
}
}
好比這樣,多個(gè)線程有可能同時(shí)去 release,所以會(huì)崩潰
解決方案,可以加鎖,在name設(shè)置前后去加鎖。
如果改成這樣呢?
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"];
});
}
就是把一個(gè)長(zhǎng)字符串變成一個(gè)短的字符創(chuàng),這時(shí)候發(fā)現(xiàn),沒有崩潰。因?yàn)楹笠粋€(gè)指針為 tagged pointer 類型的,就不存在release的操作,值直接就存儲(chǔ)再來指針的里面,直接取值就可以了,所以就不會(huì)崩潰,他就不是一個(gè)OC對(duì)象。
NSString *str = [NSString stringWithFormat:@"123"];
NSString *str1 = [NSString stringWithFormat:@"aefasfasdfasfdasf"];
NSLog(@"%@ %@",[str class],[str1 class]);
我們來打印下兩個(gè)類
NSTaggedPointerString __NSCFString
可以看到 第一個(gè)類為 tagged pointer 類,也證明了我們的猜想。
其實(shí)源碼里面是有判斷的,當(dāng)為mac 的時(shí)候 &1,當(dāng)為ios的時(shí)候&(1<<63),所以 ios 最高有效位為1就為 tagged pointer 類型,mac 最低有效位為1就為tagged pointer