iOS程序的內存布局
image.png
代碼段:編譯之后的代碼
數據段
字符串常量:比如NSString *str = @"123"
已初始化數據:已初始化的全局變量、靜態變量等
未初始化數據:未初始化的全局變量、靜態變量等棧:函數調用開銷,比如局部變量。分配的內存空間地址越來越小
堆:通過alloc、malloc、calloc等動態分配的空間,分配的內存空間地址越來越大
Tagged Pointer
- 從64bit開始,iOS引入了Tagged Pointer技術,用于優化NSNumber、NSDate、NSString等小對象的存儲
- 在沒有使用Tagged Pointer之前, NSNumber等對象需要動態分配內存、維護引用計數等,NSNumber指針存儲的是堆中NSNumber對象的地址值
- 使用Tagged Pointer之后,NSNumber指針里面存儲的數據變成了:Tag + Data,也就是將數據直接存儲在了指針中
- 當指針不夠存儲數據時,才會使用動態分配內存的方式來存儲數據
- objc_msgSend能識別Tagged Pointer,比如NSNumber的intValue方法,直接從指針提取數據,節省了以前的調用開銷
- OC對象 內存地址最低有效位都是0,內存分配(對齊)都是16的整數倍,所以最后位肯定未0.
-
objc_messageSend
內部支持Tagged Pointer
- 如何判斷一個指針是否為Tagged Pointer?
iOS平臺,最高有效位是1(第64bit)
Mac平臺,最低有效位是1
判斷是否為Tagged Pointer
image.png
image.png
思考以下2段代碼能發生什么事?有什么區別?
image.png
- (void)setName:(NSString *)name{
if (_name != name) {
[_name release];
//如果用copy,下面就是copy
//如果是strong _name = [name retain];
_name = [name copy];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
//壞內存訪問,因為有多條線程進入setter,執行release操作
//多線程可能會釋放掉_name成員變量,再次使用_name賦值時就會報錯
//因為_name已經內銷毀
for (int i = 0; i < 1000; i++) {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abcdefghijk"];
});
}
}
//解決方案:使用atomic關鍵字 加鎖
@property (atomic,copy) NSString * name;
//推薦如下寫法
//加鎖
self.name = [NSString stringWithFormat:@"abcdefghijk"];
//解鎖
為什么字符串換成abc就不會報錯
因為使用了Tagged Pointer
技術,objc_messageSend
內部會判斷是不是普通的指針,如果使用了Tagged Pointer
技術,就不是一個OC對象,會從指針中直接進行取值,而不會進入消息機制,做相應的內存管理代碼的調用.