運(yùn)行下面兩段代碼,會發(fā)生什么事?

這兩段代碼看似一致,其實(shí)結(jié)果差別很大,代碼1會崩潰(奔潰信息如下),代碼2卻不會;

從報錯信息可以看出原因是壞內(nèi)存訪問,現(xiàn)在使用的是ARC,但是ARC的底層實(shí)現(xiàn)是MRC,如下:
//self.name賦值MRC實(shí)現(xiàn)
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
由于線程不安全,多條線程同時執(zhí)行了[_name release];這行代碼,所以下一個線程進(jìn)來的時候,發(fā)現(xiàn)self.name已經(jīng)被釋放了,那么就會出現(xiàn)壞內(nèi)存訪問的情況;
那么為什么代碼2沒有崩潰呢?
這是因?yàn)閺?4bit開始,iOS引入了Tagged Pointer技術(shù),用于優(yōu)化NSNumber、NSDate、NSString等小對象的存儲,這些較小的對象,直接存儲在棧區(qū),所以線程不安全并沒有影響到棧區(qū)。
arm64架構(gòu)后指針有64位,于是把空余的位利用起來,存儲對象值,這個就是Tagged Pointer
打印查看兩字符對象的地址:

可以看出str2的地址明顯高于str1,因?yàn)闂^(qū)的地址值,要在堆區(qū)之上,可以簡易推測str2的值直接存在指針上。
詳細(xì)的Tagged Pointer介紹有好多博客