前言:
? 從64位開始,iOS引入了Tagged Pointer技術,用于優化NSNumber、NSDate、NSString等小物件的存盤,
Tagged Pointer主要為了解決兩個問題:
- 記憶體資源浪費,堆區需要額外的開辟空間
- 訪問效率,每次set/get都需要訪問堆區,浪費時間, 而且需要管理堆區物件的宣告周期,降低效率
Tagged Pointer特點:
- 專門用來存盤小物件,比如NSString,NSNumber,NSDate
Tagged Pointer指標的值不再是堆區地址,而是包含真正的值,所以它不會在堆上再開辟空間了,也不需要管理物件的生命周期了,- 記憶體讀取提升
3倍,創建比之前快100多倍,銷毀速度更快
一、引入Tagged Pointer 前后對比

1、引入前
NSNumber等物件需要動態分配記憶體、維護參考計數等, 總共的空間= 指標空間 + 堆中分配的空間
2、引入后
NSNumber等物件,只需要分配一個指標即可,這個指標內部會包含這些資料內容,
總空間 = 指標空間
因為不用去用物件的方式管理參考計數,所以省卻了 retain,release操作,
二、Tagged Pointer 原理
number1只有堆疊上的指標記憶體;而maxNum不僅有指標記憶體,在堆中還分配了32位元組的記憶體用于存盤該變數的值,通過觀察發現,物件的number1、number2、number3、number4都存盤在了對應的指標中;而maxNum不同由于資料過大,導致無法 1 個指標 8 個位元組的記憶體根本存不下,而申請了32位元組堆記憶體,
NSString型別的Tagged Pointer指標與基本型別的指標是不一樣的,末尾的數字為字串的長度;NSString型別的Tagged Pointer指標存盤char型別,回傳的是ASCII碼(該值為16進制的,需要進行十進制轉換)
三、如何判斷是否使用了 Tagged Pointer 技術
BOOL isTaggedPointer(id pointer) {
return (long)(__bridge void *)pointer & 1;
}
該函式就是呼叫了isTaggedPointer,
四、使用 Tagged Pointer 注意點
? 我們知道,所有OC物件都有isa指標,而Tagged Pointer并不是真正的物件,它沒有isa指標,所以如果你直接訪問Tagged Pointer的isa成員的話,在編譯時將會有警告,
五、面試題
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abcdefghijk"];
});
}
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"];
});
}
兩者運行結果有何不同?
首先看self.name = [NSString stringWithFormat:@"abcdefghijk"];

崩潰,并且崩潰在objc_release的地方,
是什么原因導致崩潰的呢?
我們知道,
self.name = [NSString stringWithFormat:@"abcdefghijk"];
其實是呼叫了
[self setName:[NSString stringWithFormat:@"abcdefghijk"]];
而setName:的實作是:
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];//老的釋放掉
_name = [name copy];//傳入的值copy后賦值給_name
}
}
由于是async異步操作,self.name = [NSString stringWithFormat:@"abcdefghijk"];即[_name release];有可能會被多條執行緒同時操作,導致,執行緒n把_name釋放掉,執行緒n+1又要執行_name的釋放,從而造成_name已經被釋放兩次,第二次訪問的時候,_name已經釋放過,造成壞記憶體訪問,
解決方法一:atomic
@property (copy, atomic) NSString *name;
從而:
- (void)setName:(NSString *)name
{
//加鎖操作
if (_name != name) {
[_name release];
_name = [name copy];
}
//解鎖操作
}
解決方法二:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<1000; i++) {
dispatch_async(queue, ^{
//加鎖
self.name = [NSString stringWithFormat:@"abcdefghijk"];
});
//解鎖
}
self.name = [NSString stringWithFormat:@"abc"];
為何沒有崩潰呢?


從型別可以看出來,
內容多的name型別是__NSCFString
內容少的name型別是NSTaggedPointerString

這就是原因所在,
內容少的name,由于型別是NSTaggedPointerString,在賦值的時候
是直接在指標里面取值,而不需要release操作,因此,不會崩潰
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/500477.html
標籤:iOS
