NSString內(nèi)存管理

NSString的內(nèi)存優(yōu)化

OC中的NSString不論是在編譯時(shí)還是在運(yùn)行時(shí)都做了很多的優(yōu)化股淡,并不同于普通的對(duì)象叛赚,它是一個(gè)非常復(fù)雜的存在虫几。

首先定義幾個(gè)宏定義方便打印觀察結(jié)果:

#if __has_feature(objc_arc)
#define Obj_RetainCount(obj) \
CFGetRetainCount((__bridge CFTypeRef)(obj))
#else
#define Obj_RetainCount(obj) \
DebugLog(@"%lu",[obj retainCount]);
#endif
#define XFLog(_var) NSLog(@"%@ : class = %@ p = %p retainCount = %d",@#_var,NSStringFromClass([_var class]),_var,Obj_RetainCount(_var));

測(cè)試代碼如下:

NSString *a = @"str";
NSString *b = [[NSString alloc]init];
NSString *c = [[NSString alloc]initWithString:@"str"];
NSString *d = [[NSString alloc]initWithFormat:@"str"];
NSString *e = [NSString stringWithFormat:@"str"];
NSString *f = [NSString stringWithFormat:@"123456789"];
NSString *g = [NSString stringWithFormat:@"1234567890"];

XFLog(a);
XFLog(b);
XFLog(c);
XFLog(d);
XFLog(e);
XFLog(f);
XFLog(g);
打印結(jié)果:

a : class = __NSCFConstantString p = 0x10eb5d090 retainCount = -1
b : class = __NSCFConstantString p = 0x10ee90470 retainCount = -1
c : class = __NSCFConstantString p = 0x10eb5d090 retainCount = -1
d : class = NSTaggedPointerString p = 0xa000000007274733 retainCount = -1
e : class = NSTaggedPointerString p = 0xa000000007274733 retainCount = -1
f : class = NSTaggedPointerString p = 0xa1ea1f72bb30ab19 retainCount = -1
g : class = __NSCFString p = 0x7ff449c2a5c0 retainCount = 2

可以看到肺稀,不同方式創(chuàng)建的字符串類(lèi)型不同,引用計(jì)數(shù)也有所區(qū)別丁稀,并不是我們常規(guī)理解的對(duì)象初始化后引用計(jì)數(shù)為1吼拥。創(chuàng)建的字符串有3種類(lèi)型

  • __NSCFConstantString
  • __NSCFString
  • NSTaggedPointerString

造成這種結(jié)果的原因是由于OC對(duì)字符串做的內(nèi)存優(yōu)化。

__NSCFConstantString

對(duì)變量類(lèi)型名上就可以看出线衫,這種類(lèi)型的字符串是常量字符串凿可。該類(lèi)型的字符串以字面量的方式創(chuàng)建,保存在字符串常量區(qū)授账,是在編譯時(shí)創(chuàng)建的枯跑。例如:

NSString *a = @"good afternoon!";
NSString *b = [[NSString alloc]initWithString:@"good afternoon!"];

對(duì)于 initWithString 實(shí)例方法以及 stringWithString 類(lèi)方法,編譯器會(huì)給出redundant警告,原因是該方法創(chuàng)建字符串等同于直接復(fù)制字符串字面量白热。

當(dāng)創(chuàng)建的字符串變量值在常量區(qū)已經(jīng)存在時(shí)敛助,會(huì)指向那個(gè)字符串,這是編譯器做的優(yōu)化。
由于是常量屋确,因此其內(nèi)存管理并不同于對(duì)象的內(nèi)存管理纳击,引用計(jì)數(shù)用整形格式打出來(lái)始終為-1。

__NSCFString

__NSCFString 表示對(duì)象類(lèi)型的字符串攻臀,在運(yùn)行時(shí)創(chuàng)建焕数,保存在堆區(qū),初始引用計(jì)數(shù)為1茵烈,其內(nèi)存管理方式就是對(duì)象的內(nèi)存管理方式百匆。該種類(lèi)型字符串通過(guò)format方式創(chuàng)建,并且字符串內(nèi)容僅由數(shù)字呜投、字母和常規(guī)ASCII字符構(gòu)成加匈,且其長(zhǎng)度不能太小,否則創(chuàng)建的是NSTaggedPointerString類(lèi)型仑荐。

NSString *d = [[NSString alloc]initWithFormat:@"我是對(duì)象"];
NSString *e = [NSString stringWithFormat:@"1234567890"]; //__NSCFString

NSTaggedPointerString

NSTaggedPointerString 類(lèi)型的字符串是對(duì)__NSCFString類(lèi)型的一種優(yōu)化雕拼,在運(yùn)行時(shí)創(chuàng)建字符串時(shí),會(huì)對(duì)字符串內(nèi)容及長(zhǎng)度作判斷粘招,若內(nèi)容由ASCII字符構(gòu)成且長(zhǎng)度較猩犊堋(具體要多小暫時(shí)不太清楚),這時(shí)候創(chuàng)建的字符串類(lèi)型就是 NSTaggedPointerString (標(biāo)簽指針字符串)洒扎,字符串直接存儲(chǔ)在指針的內(nèi)容中(這個(gè)不太理解)辑甜。NSTaggedPointerString 類(lèi)型的字符串引用計(jì)數(shù)同樣為-1,不適用對(duì)象的內(nèi)存管理策略袍冷。

NSString *e = [NSString stringWithFormat:@"123456789"]; //NSTaggedPointerString 

而上面以同樣的方式創(chuàng)建的1234567890字符串卻為 __NSCFString 類(lèi)型磷醋。

探究NSDictionary NSArray NSValue NSNumber是否也做了優(yōu)化

NSDictionary測(cè)試代碼如下:

NSDictionary *d1 = @{@"aa":@"11",@"bb":@"22"};
NSDictionary *d2 = [[NSDictionary alloc]init];
NSDictionary *d3 = [[NSDictionary alloc]initWithObjectsAndKeys:@"a",@"1", nil];
NSDictionary *d4 = [NSDictionary dictionaryWithObjectsAndKeys:@"b",@"2", nil];

XFLog(d1);
XFLog(d2);
XFLog(d3);
XFLog(d4);
打印結(jié)果:

d1 : class = __NSDictionaryI p = 0x7fddbad1e9d0 retainCount = 1
d2 : class = __NSDictionary0 p = 0x7fddbae00dd0 retainCount = -1
d3 : class = __NSDictionaryI p = 0x7fddbad044f0 retainCount = 1
d4 : class = __NSDictionaryI p = 0x7fddbadbd8a0 retainCount = 2

NSArray測(cè)試代碼如下:

NSArray *a1 = @[@"1",@"2"];
NSArray *a2 = [[NSArray alloc]init];
NSArray *a3 = [[NSArray alloc]initWithObjects:@"1", nil];
NSArray *a4 = [[NSArray alloc]initWithArray:@[@"a",@"b"]];
NSArray *a5 = [NSArray arrayWithObjects:@"m",@"n", nil];

XFLog(a1);
XFLog(a2);
XFLog(a3);
XFLog(a4);
XFLog(a5);
打印結(jié)果:

a1 : class = __NSArrayI p = 0x7fddbae16970 retainCount = 1
a2 : class = __NSArray0 p = 0x7fddbae00ce0 retainCount = -1
a3 : class = __NSArrayI p = 0x7fddbae23ea0 retainCount = 1
a4 : class = __NSArrayI p = 0x7fddbae0ab30 retainCount = 1
a5 : class = __NSArrayI p = 0x7fddbae0ab10 retainCount = 2

NSValue測(cè)試代碼如下:

NSValue *v1 = [NSValue valueWithCGPoint:CGPointMake(10, 10)];
CGPoint point = CGPointMake(20, 20);
NSValue *v2 = [[NSValue alloc]initWithBytes:&point objCType:@encode(CGPoint)];
XFLog(v1);
XFLog(v2);
打印結(jié)果:

v1 : class = NSConcreteValue p = 0x7fddbae153e0 retainCount = 2
v2 : class = NSConcreteValue p = 0x7fddbae21fa0 retainCount = 1

NSNumber測(cè)試代碼如下:

NSNumber *n1 = [NSNumber numberWithInt:123456789];
NSNumber *n2 = [NSNumber numberWithInt:1234567890];
int i = 10;
NSNumber *n3 = [[NSNumber alloc]initWithBytes:&i objCType:@encode(int)];
XFLog(n1);
XFLog(n2);
XFLog(n3);
打印結(jié)果:

n1 : class = __NSCFNumber p = 0xb000000075bcd152 retainCount = -1
n2 : class = __NSCFNumber p = 0xb000000499602d22 retainCount = -1
n3 : class = NSConcreteValue p = 0x7fddbadb4df0 retainCount = 1

結(jié)論:

對(duì)于NSDictionary、NSArray以及NSValue實(shí)例胡诗,除了空字典和空字符串邓线,其余實(shí)例都遵循對(duì)象的內(nèi)存管理策略淌友。
NSNumber類(lèi)創(chuàng)建的對(duì)于普通數(shù)據(jù)類(lèi)型的封裝的實(shí)例,其內(nèi)存管理同樣做了優(yōu)化骇陈,不遵循對(duì)象的內(nèi)存管理策略震庭。
另外可以看到,有些新創(chuàng)建的對(duì)象引用計(jì)數(shù)為1你雌,有些為2器联。這是因?yàn)橛妙?lèi)方法創(chuàng)建的實(shí)例,系統(tǒng)自動(dòng)將其置入自動(dòng)釋放池匪蝙。

mutable variable

測(cè)試代碼如下:

//NSMutableString
NSMutableString *ms1 = [[NSMutableString alloc]init];
NSMutableString *ms2 = [[NSMutableString alloc]initWithString:@"str"];
NSMutableString *ms3 = [[NSMutableString alloc]initWithFormat:@"str"];
NSMutableString *ms4 = [NSMutableString stringWithFormat:@"str"];
NSMutableString *ms5 = [NSMutableString stringWithFormat:@"123456789"];
NSMutableString *ms6 = [NSMutableString stringWithFormat:@"1234567890"];

XFLog(ms1);
XFLog(ms2);
XFLog(ms3);
XFLog(ms4);
XFLog(ms5);
XFLog(ms6);
//NSMutableDictionary
NSMutableDictionary *md1 = [[NSMutableDictionary alloc]init];
NSMutableDictionary *md2 = [[NSMutableDictionary alloc]initWithObjectsAndKeys:@"a",@"1", nil];
NSMutableDictionary *md3 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"b",@"2", nil];

XFLog(md1);
XFLog(md2);
XFLog(md3);
//NSMutableArray
NSMutableArray *ma1 = [[NSMutableArray alloc]init];
NSMutableArray *ma2 = [[NSMutableArray alloc]initWithObjects:@"1", nil];
NSMutableArray *ma3 = [[NSMutableArray alloc]initWithArray:@[@"a",@"b"]];
NSMutableArray *ma4 = [NSMutableArray arrayWithObjects:@"m",@"n", nil];

XFLog(ma1);
XFLog(ma2);
XFLog(ma3);
XFLog(ma4);
測(cè)試結(jié)果如下:

ms1 : class = __NSCFString p = 0x7fd31070e6b0 retainCount = 1
ms2 : class = __NSCFString p = 0x7fd310716d40 retainCount = 1
ms3 : class = __NSCFString p = 0x7fd310719910 retainCount = 1
ms4 : class = __NSCFString p = 0x7fd31071b610 retainCount = 2
ms5 : class = __NSCFString p = 0x7fd31071bc40 retainCount = 2
ms6 : class = __NSCFString p = 0x7fd310715350 retainCount = 2
md1 : class = __NSDictionaryM p = 0x7fd31071be00 retainCount = 1
md2 : class = __NSDictionaryM p = 0x7fd31070c270 retainCount = 1
md3 : class = __NSDictionaryM p = 0x7fd310717960 retainCount = 2
ma1 : class = __NSArrayM p = 0x7fd31060d860 retainCount = 1
ma2 : class = __NSArrayM p = 0x7fd310605730 retainCount = 1
ma3 : class = __NSArrayM p = 0x7fd310607f80 retainCount = 1
ma4 : class = __NSArrayM p = 0x7fd310606dc0 retainCount = 2

結(jié)論:

可變變量實(shí)例均以對(duì)象的形式保存在堆中主籍。

————————————————

轉(zhuǎn)載文章:NSString內(nèi)存管理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市逛球,隨后出現(xiàn)的幾起案子千元,更是在濱河造成了極大的恐慌,老刑警劉巖颤绕,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幸海,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奥务,警方通過(guò)查閱死者的電腦和手機(jī)物独,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氯葬,“玉大人挡篓,你說(shuō)我怎么就攤上這事≈愠疲” “怎么了官研?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)闯睹。 經(jīng)常有香客問(wèn)我戏羽,道長(zhǎng),這世上最難降的妖魔是什么楼吃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任始花,我火速辦了婚禮,結(jié)果婚禮上孩锡,老公的妹妹穿的比我還像新娘酷宵。我一直安慰自己,他們只是感情好躬窜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布忧吟。 她就那樣靜靜地躺著,像睡著了一般斩披。 火紅的嫁衣襯著肌膚如雪溜族。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,954評(píng)論 1 283
  • 那天垦沉,我揣著相機(jī)與錄音煌抒,去河邊找鬼。 笑死厕倍,一個(gè)胖子當(dāng)著我的面吹牛寡壮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播讹弯,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼况既,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了组民?” 一聲冷哼從身側(cè)響起棒仍,我...
    開(kāi)封第一講書(shū)人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臭胜,沒(méi)想到半個(gè)月后莫其,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耸三,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年乱陡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仪壮。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡憨颠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出积锅,到底是詐尸還是另有隱情爽彤,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布乏沸,位于F島的核電站淫茵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蹬跃。R本人自食惡果不足惜匙瘪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蝶缀。 院中可真熱鬧丹喻,春花似錦、人聲如沸翁都。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)柄慰。三九已至鳍悠,卻和暖如春税娜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背藏研。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工敬矩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蠢挡。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓弧岳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親业踏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子禽炬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容