先来看一个例子:
#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);
输出结果为:
<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>
可以看到,不同方式创建的字符串类型不同,引用计数也有所区别,并不是我们常规理解的对象初始化后引用计数为1。创建的字符串有3种类型
- __NSCFConstantString
- __NSCFString
- __NSTaggedPointerString
造成这种结果的原因是由于OC对字符串做的内存优化。
__NSCFConstantString
对变量类型名上就可以看出,这种类型的字符串是常量字符串。该类型的字符串以字面量的方式创建,保存在字符串常量区,是在编译时创建的。如上a,b,c,打印结果:
<pre>
class = __NSCFConstantString p = 0x107105b00 retainCount = 1152921504606846975
</pre>
对于 initWithString 实例方法以及 stringWithString 类方法,编译器会给出redundant警告,原因是该方法创建字符串等同于直接复制字符串字面量.
当创建的字符串变量值在常量区已经存在时,会指向那个字符串,这是编译器做的优化。
由于是常量,因此其内存管理并不同于对象的内存管理,引用计数用整形格式打出来始终为-1。(此处1152921504606846975打印为%ld,对象的引用计数是64位OS下无符号长整型的最大值,后面不做解释)。
__NSCFString
__NSCFString 表示对象类型的字符串,在运行时创建,保存在堆区,初始引用计数为1,其内存管理方式就是对象的内存管理方式。该种类型字符串通过format方式创建,并且字符串内容仅由数字、字母和常规ASCII字符构成,且其长度>=10,否则创建的是NSTaggedPointerString类型。
如上h,i打印结果:
<pre>
h : class = __NSCFString p = 0x600003f33580 retainCount = 2
i : class = __NSCFString p = 0x600003f32800 retainCount = 1
</pre>
可见当以format创建出来的字符串>9个时,会创建NSCFString类型,引用计数从1开始计算。
此处
个人的理解:format后面的字符串>9创建在堆上,引用计数初始为1,stringWith是对该字符串的引用,因此i的retainCount = 2,而h新获得的字符串是alloc init,重新开辟的内存,所以retainCount = 1。
NSTaggedPointerString
NSTaggedPointerString 类型的字符串是对__NSCFString类型的一种优化,在运行时创建字符串时,会对字符串内容及长度作判断,若内容由ASCII字符构成且长度<10,这时候创建的字符串类型就是 NSTaggedPointerString (标签指针字符串),字符串直接存储在指针的内容中。NSTaggedPointerString 类型的字符串引用计数同样为-1,不适用对象的内存管理策略。
<pre>
class = NSTaggedPointerString p = 0xebeeae3f52cf0333 retainCount = 9223372036854775807
</pre>