nsstring 内存分析

NSString 内存

NSString 的 几种类型

  1. __NSCFConstantString Constant [ˈkɑːnstənt] 常量,常数
  2. NSTaggedPointerString
  3. __NSCFString

实例代码


    #import <malloc/malloc.h> // 引用头文件

    #define StrLog(_c) NSLog(@"引用计数:%@ -- 堆中实际分配: %zd -- 地址:%p -- 类型:%@",[_c valueForKey:@"retainCount"], malloc_size((__bridge const void *)(_c)), _c, [_c class]);

    NSString *str1 = @"123456789"; // __NSCFConstantString
    StrLog(str1);
    
    NSString *str2 = @"1234567890"; // __NSCFConstantString
    StrLog(str2);
    
    NSString *str3 = @"12345678901212345678901212345678901212345sasasa"; // __NSCFConstantString
    StrLog(str3);
    
    NSString *str4 = [NSString stringWithFormat:@"123456789"]; // NSTaggedPointerString
    StrLog(str4);
    
    NSString *str5 = [NSString stringWithFormat:@"1234567890"]; // __NSCFString
    StrLog(str5);
    
    NSString *str6 = [NSString stringWithFormat:@"12345678901212345678901212345678901212345sasasa"]; // __NSCFString
    StrLog(str6);

打印结果

 引用计数:18446744073709551615 -- 堆中实际分配: 0 -- 地址:0x10b3f3f00 -- 类型:__NSCFConstantString
 
 引用计数:18446744073709551615 -- 堆中实际分配: 0 -- 地址:0x10b3f3f60 -- 类型:__NSCFConstantString
 
 引用计数:18446744073709551615 -- 堆中实际分配: 0 -- 地址:0x10b3f3f80 -- 类型:__NSCFConstantString
 
 引用计数:18446744073709551615 -- 堆中实际分配: 0 -- 地址:0x953b7922756a4456 -- 类型:NSTaggedPointerString
 
 引用计数:2 -- 堆中实际分配: 32 -- 地址:0x600002545880 -- 类型:__NSCFString
 
 引用计数:2 -- 堆中实际分配: 80 -- 地址:0x600000625ea0 -- 类型:__NSCFString

1、引用计数器的值为18446744073709551615,即为2^64-1,当引用计数器的值为-1或者无穷大的时候,说明不满足内存管理法则,既系统并不是通过内存黄金法则来释放的。也就是对str10进行retain,release操作都是没有用的。
2、str10对象在堆中实际没有分配空间,只有存放在堆区中的对象才满足内存管理法则。
3、str10对象的地址是低地址,排除代码区和常量区,所以我猜测__NSCFConstantString类型的对象存放在 初始化过的静态区:data段,继续往下走我们会验证这一猜想。

33217A46-A84E-4454-B11F-6FC47A56597D
引用计数:18446744073709551615 -- 堆中实际分配: 0 -- 地址:0x103883f00 -- 类型:__NSCFConstantString
(lldb) p/x str1
(__NSCFConstantString *) $0 = 0x0000000103883f00 @"123456789"
(lldb) p/x str1->isa
(Class) $1 = 0x00000001050ed5c0 __NSCFConstantString
C6F734F7-10FE-45F6-8E72-E6AD514AFF79
6A020E56-4E9C-4416-9CBA-E8546491D361
  • 0~8 字节---> C0 D5 0E 05 01 00 00 00 isa指针
  • 9~16 字节--> C8 07 00 00 00 00 00 00 一脸懵逼 8个字节可能会存放字符串的编码方式,以及对象类型
  • 17~24 字节-> 53 8A 87 03 01 00 00 00 看起来像个地址
  • 25~32字节-> 09 00 00 00 00 00 00 00 09

打印 0x0000000103878a53 的地址

C60E3C7C-0CD0-45C4-8F8F-E79E10695AD0

0x10387110e

1、__NSCFConstantString类型的对象是特殊对象。其特殊性体现在可以用static修饰,存放在data段,不满足内存管理法则。
2、所有@"....."都会在内存中生成__NSCFConstantString类型的对象。
3、__NSCFConstantString类型的对象在内存中占用32个字节,用来存放isa指针,(编码形式,对象类型),字符串常量的地址以及字符串长度。
4、字符串的值(字符串常量)存放在文字常量区。

    NSString *str7 = [[NSString alloc] initWithString:@"222222222222222222222"];
    StrLog(str7);
    
    // Using 'initWithString:' with a literal is redundant
    
    NSString *str8 = [NSString stringWithString:@"2222222222222222"];
    StrLog(str8);
    
    // Using 'stringWithString:' with a literal is redundant
    
 引用计数:18446744073709551615 -- 堆中实际分配: 0 -- 地址:0x10b12efa0 -- 类型:__NSCFConstantString
 引用计数:18446744073709551615 -- 堆中实际分配: 0 -- 地址:0x10b12efc0 -- 类型:__NSCFConstantString

上边也是 静态区

但是 stringWithFormat 不一样,因为可以拼接 所以 字数少的情况下 放到栈上的 NSTaggedPointerString,字数多的情况下,放到堆上的 __NSCFString

NSString 类簇

继承关系
__NSCFConstantString -> __NSCFString -> NSMutableString -> NSString -> NSObject

NSTaggedPointerString -> NSString -> NSObject

一个讲TaggedPointer的文章

https://www.jianshu.com/p/a5b6e5838b0a

https://www.jianshu.com/p/640f173b5583