NSString 的一点备忘

2,290 阅读4分钟

关于字符串查阅了一些资料,做一点总结,以作备忘.

#define KLog(_c) NSLog(@"%@ -- %p -- %@",_c,_c,[_c class]);

可以把字符串的初始化方式分为了两类: 一类是后面是带withString的(直接=@"xx"这种归入此类)

一类是后面带withFormat的

第一类

NSString * __weak s1 = @"111";
NSString * __weak s2 =[[NSString alloc] initWithString:@"222"];
NSString * __weak s3 = [NSString stringWithString:@"33"];
KLog(s1)
KLog(s2)
KLog(s3)

输出

这几种方式创建出来的string,他实际属于__NSCFConstantString类,__NSCFConstantString代表的是一个字符串常量,retain和copy操作都还是自己,没有任何变化。所以说__NSCFConstantString类的字符串是创建了就不能被我们释放的。

**如果__NSCFConstantString类的字符串的内容都是一样的,那么不论怎么创建,他们的地址都是一样的。应该是系统对内存做了优化. **

第二类

NSString *  s1 = [NSString stringWithFormat:@"123456789"];
KLog(s1)

NSString *  s2 = [NSString stringWithFormat:@"1234567890"];
KLog(s2)

NSString *  s3 = [[NSString alloc] initWithFormat:@"123456789"];
KLog(s3)

NSString *  s4 = [[NSString alloc] initWithFormat:@"1234567890"];
KLog(s4)

这里我们可以看到两种类型,字符串长度>9的时候,字符创的类型是__NSCFString,<=9时是NSTaggedPointerString类型。所以在测试时,分别测试长度为9和10两种情况。

__NSCFString 表示对象类型的字符串,我们可以把他理解为正常的符合内存管理规则的那种字符串。该种类型字符串通过format方式创建。

NSTaggedPointerString 类型的字符串是对__NSCFString类型的一种优化,在运行时创建字符串时,会对字符串内容及长度作判断,若内容由ASCII字符构成且长度较小(具体要多小暂时不太清楚),这时候创建的字符串类型就是 NSTaggedPointerString (标签指针字符串),字符串直接存储在指针的内容中。

以下是一些关于NSTaggedPointerString的资料:

为了改进上面提到的内存占用和效率问题,苹果提出了Tagged Pointer对象。由于NSNumber、NSDate一类的变量本身的值需要占用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达到21亿多(2^31)。所以我们可以将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。所以,实际上它不再是一个对象了,它只是披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free。假设你调用NSNumber的integerValue,它将从数据部分中提取数值并返回。这样,每访问一个对象,就省下了一次真正对象的内存分配,省下了一次间接取值的时间。同时引用计数可以是空指令,因为没有内存需要释放。对于常用的类,这将是一个巨大的性能提升。引入Tagged Pointer对象后,64位CPU下NSNumber内存图如下:

网上大神的图

NSTaggedPointerString类也不符合内存管理的规则。

我们继续进行测试:

   NSString *  s1 = [NSString stringWithFormat:@"123456789"];
   // [s1 release];
   KLog(s1)
   NSString *  s2 = [NSString stringWithFormat:@"1234567890"];
   [s2 release];
   KLog(s2)

可以看到我们对s2 进行[s2 release]操作,s2是__NSCFSrting类型的字符串,适用常规的字符串内存管理规则,但是程序crash了.其实用 stringWithFormat:创建的 __NSCFSrting类型的字符串是引用计数不会+1,这时候如果进行[s2 release]操作会造成过度释放,造成crash. 而s1 是NSTaggedPointerString类型的字符串,不适用于普通字符串的内存管理规则,因此即使 [s1 release]也不会造成crash.

以下做一点扩展提醒

stringWithFormat创建的__NSCFSrting类型的字符串对象会被添加到自动释放池是自动释放对象,而initWithFormat创建的对象不会被添加到自动释放池。不只是NSString类,其他类都是如此的。ARC下生成的对象不能调用autorelease,ARC下为了区分生成的对象是不是autorelease,就确立了硬性规则。这些规则简单地体现在了方法名上。 使用alloc/new/copy/mutableCopy名称开头的方法意味着生成的对象调用者持有,这些自己生成并持有的对象通过release释放,而其他名称类似string array dictionary的方法生成对象,不归调用者持有,这种情况下,对象是自动释放。 另外当字符串的内容有中文或者特殊字符(非 ASCII 字符)时,只能用__NSCFSrting类对象存储。字面量字符串不会使用Tagged Pointer。

以上是我查阅一些资料和自己的一些总结,以作备忘.希望对你们也有帮助.