一. Tagged Pointer應用場景
ViewController
@interface ViewController ()
@property (strong, nonatomic) NSString *name;
@end
以下2段代碼能發(fā)生什么事?有什么區(qū)別?
1. 片段1
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abcfdfdgfdgdffd"];
});
}
2. 片段2
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abc"];
});
}
結論:
-
片段1執(zhí)行會引起異常,片段2正常執(zhí)行。 -
片段1是并發(fā)給name屬性賦值,就是同時很多個線程訪問name的set方法,因為name是使用nonatomic修飾的,不是線程安全的,相當于同時訪問以下代碼,可能導致多次釋放name,所以會拋異常。
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
解決方法:
a) 使用atomic修飾name屬性
b) 在dispatch_async里邊加鎖
c) 使用串行隊列
- 片段2是使用的Tagged Pointer技術,直接將數(shù)據(jù)存儲在指針當中,所以
self.name根本不存在釋放堆空間內(nèi)存的操作,所以不會有問題。
二. Tagged Pointer介紹

image.png
- 從64bit開始,iOS引入了Tagged Pointer技術,用于優(yōu)化NSNumber、NSDate、NSString等小對象的存儲
- 在沒有使用Tagged Pointer之前, NSNumber等對象需要動態(tài)分配內(nèi)存、維護引用計數(shù)等,NSNumber指針存儲的是堆中NSNumber對象的地址值
- 使用Tagged Pointer之后,NSNumber指針里面存儲的數(shù)據(jù)變成了:Tag + Data,也就是將數(shù)據(jù)直接存儲在了指針中
- 當指針不夠存儲數(shù)據(jù)時,才會使用動態(tài)分配內(nèi)存的方式來存儲數(shù)據(jù)
- objc_msgSend能識別Tagged Pointer,比如NSNumber的intValue方法,直接從指針提取數(shù)據(jù),節(jié)省了以前的調(diào)用開銷
- 如何判斷一個指針是否為Tagged Pointer?
a) iOS平臺,最高有效位是1(第64bit)
b) Mac平臺,最低有效位是1