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ì)象的形式保存在堆中主籍。
————————————————