先來看一個例子:
#if __has_feature(objc_arc)
#define Obj_RetainCount(obj) CFGetRetainCount((__bridge CFTypeRef)(obj))
#else
#define Obj_RetainCount(obj) DebugLog(@"%lu",[obj retainCount]);
#endif
#define HFLog(_var) \
NSLog(@"%@ : class = %@ p = %p retainCount = %ld",@#_var,NSStringFromClass([_var class]),_var,\
(unsigned long)Obj_RetainCount(_var));
NSString *a = @"str";
NSString *b = [[NSString alloc] init];
NSString *c = [[NSString alloc] initWithString:@"str"];
NSString *d = [[NSString alloc] initWithFormat:@"<10"];
NSString *e = [NSString stringWithFormat:@"<10"];
NSString *f = [d copy];
NSString *g = [a copy];
NSString *h = [[NSString alloc] initWithFormat:@"1234567890"];
NSString *i = [NSString stringWithFormat:@"1234567890"];
NSMutableString *mString1 = [NSMutableString stringWithString:@"<10"];
NSMutableString *mString2 = [NSMutableString stringWithFormat:@"<10"];
NSMutableString *mString3 = [[NSMutableString alloc] initWithFormat:@"1234567890"];
HFLog(a);
HFLog(b);
HFLog(c);
HFLog(d);
HFLog(e);
HFLog(f);
HFLog(g);
HFLog(h);
HFLog(i)
HFLog(mString1);
HFLog(mString2);
HFLog(mString3);
輸出結(jié)果為:
<pre>
a : class = __NSCFConstantString p = 0x107105b00 retainCount = 1152921504606846975
b : class = __NSCFConstantString p = 0x107f62178 retainCount = 1152921504606846975
c : class = __NSCFConstantString p = 0x107105b00 retainCount = 1152921504606846975
d : class = NSTaggedPointerString p = 0xebeeae3f52cf0333 retainCount = 9223372036854775807
e : class = NSTaggedPointerString p = 0xebeeae3f52cf0333 retainCount = 9223372036854775807
f : class = NSTaggedPointerString p = 0xebeeae3f52cf0333 retainCount = 9223372036854775807
g : class = NSTaggedPointerString p = 0xebeeae3f52cf0333 retainCount = 9223372036854775807
h : class = __NSCFString p = 0x60000168f3c0 retainCount = 1
i : class = __NSCFString p = 0x60000168f3e0 retainCount = 2
mString1 : class = __NSCFString p = 0x60000310cb70 retainCount = 1
mString2 : class = __NSCFString p = 0x60000310d260 retainCount = 2
mString3 : class = __NSCFString p = 0x60000310d140 retainCount = 1
</pre>
可以看到没卸,不同方式創(chuàng)建的字符串類型不同泥技,引用計數(shù)也有所區(qū)別伦连,并不是我們常規(guī)理解的對象初始化后引用計數(shù)為1酵使。創(chuàng)建的字符串有3種類型
- __NSCFConstantString
- __NSCFString
- __NSTaggedPointerString
造成這種結(jié)果的原因是由于OC對字符串做的內(nèi)存優(yōu)化。
__NSCFConstantString
對變量類型名上就可以看出对省,這種類型的字符串是常量字符串蝗拿。該類型的字符串以字面量的方式創(chuàng)建,保存在字符串常量區(qū)蒿涎,是在編譯時創(chuàng)建的哀托。如上a,b,c,打印結(jié)果:
<pre>
class = __NSCFConstantString p = 0x107105b00 retainCount = 1152921504606846975
</pre>
對于 initWithString 實例方法以及 stringWithString 類方法,編譯器會給出redundant警告,原因是該方法創(chuàng)建字符串等同于直接復(fù)制字符串字面量.
當創(chuàng)建的字符串變量值在常量區(qū)已經(jīng)存在時劳秋,會指向那個字符串,這是編譯器做的優(yōu)化仓手。
由于是常量,因此其內(nèi)存管理并不同于對象的內(nèi)存管理玻淑,引用計數(shù)用整形格式打出來始終為-1嗽冒。(此處1152921504606846975打印為%ld,對象的引用計數(shù)是64位OS下無符號長整型的最大值补履,后面不做解釋)添坊。
__NSCFString
__NSCFString 表示對象類型的字符串,在運行時創(chuàng)建箫锤,保存在堆區(qū)贬蛙,初始引用計數(shù)為1雨女,其內(nèi)存管理方式就是對象的內(nèi)存管理方式。該種類型字符串通過format方式創(chuàng)建阳准,并且字符串內(nèi)容僅由數(shù)字氛堕、字母和常規(guī)ASCII字符構(gòu)成,且其長度>=10溺职,否則創(chuàng)建的是NSTaggedPointerString類型岔擂。
如上h,i打印結(jié)果:
<pre>
h : class = __NSCFString p = 0x600003f33580 retainCount = 2
i : class = __NSCFString p = 0x600003f32800 retainCount = 1
</pre>
可見當以format創(chuàng)建出來的字符串>9個時,會創(chuàng)建NSCFString類型浪耘,引用計數(shù)從1開始計算。
此處
個人的理解:format后面的字符串>9創(chuàng)建在堆上塑崖,引用計數(shù)初始為1七冲,stringWith是對該字符串的引用,因此i的retainCount = 2规婆,而h新獲得的字符串是alloc init,重新開辟的內(nèi)存澜躺,所以retainCount = 1。
NSTaggedPointerString
NSTaggedPointerString 類型的字符串是對__NSCFString類型的一種優(yōu)化抒蚜,在運行時創(chuàng)建字符串時掘鄙,會對字符串內(nèi)容及長度作判斷,若內(nèi)容由ASCII字符構(gòu)成且長度<10嗡髓,這時候創(chuàng)建的字符串類型就是 NSTaggedPointerString (標簽指針字符串)操漠,字符串直接存儲在指針的內(nèi)容中。NSTaggedPointerString 類型的字符串引用計數(shù)同樣為-1饿这,不適用對象的內(nèi)存管理策略浊伙。
<pre>
class = NSTaggedPointerString p = 0xebeeae3f52cf0333 retainCount = 9223372036854775807
</pre>