iOS开发-NSString类簇探究

前言 

在 Objective-C 的 Foundation 框架中 NSString 对象是很复杂的存在,使用了抽象工厂模式,因此 NSString 其实是多个类簇组合成的抽象类,不同的创建方式以及不同的字符长度都可能影响最终得到的类簇类型,今天就来研究一下 NSString 下复杂的类簇。

为了观察 NSString 的内存管理情况,选择关闭 ARC 使用 MRC 来进行测试,以观察其引用计数等状况。

#define SSLog(_var) ({ NSString *name = @#_var; NSLog(@"\n%@ : %@ : %@ -> %p : %lu", name, _var, [_var class], _var, [_var retainCount]); })

1、直接赋值创建

NSString *str  = @"";
NSString *str1 = @"11";
NSString *str2 = @"2222";
NSString *str3 = @"333333333";
NSString *str4 = @"4444444444444444";
NSString *str5 = @"sfsdfnsdfn";
NSString *str6 = @"打算多久但是";
log:
str  :  : __NSCFConstantString -> 0x100729550 : 18446744073709551615
str1 : 11 : __NSCFConstantString -> 0x100729570 : 18446744073709551615
str2 : 2222 : __NSCFConstantString -> 0x100729590 : 18446744073709551615
str3 : 333333333 : __NSCFConstantString -> 0x1007295b0 : 18446744073709551615
str4 : 4444444444444444 : __NSCFConstantString -> 0x1007295d0 : 18446744073709551615
str5 : sfsdfnsdfn : __NSCFConstantString -> 0x10d7ec550 : 18446744073709551615
str6 : 打算多久但是 : __NSCFConstantString -> 0x10d7ec570 : 18446744073709551615

可以从打印信息中看到,使用直接赋值的方法基本得到都是 __NSCFConstantString,而且引用计数固定为18446744073709551615,这说明 __NSCFConstantString 得到的都是常量字符串,存放在内存常量区,程序运行期间无法释放,即便对其进行 release 操作,引用计数也不会产生任何变化。在创建相同内容的字符时,得到的内存地址也相同。

注: initWithString 和 stringWithString 与直接赋值结果一样就不再展示。

2、initWithFormat 创建

NSString *str1 = [[NSString alloc] initWithFormat:@""];
NSString *str2 = [[NSString alloc] initWithFormat:@"11"];
NSString *str3 = [[NSString alloc] initWithFormat:@"2222"];
NSString *str4 = [[NSString alloc] initWithFormat:@"333333333"];
NSString *str5 = [[NSString alloc] initWithFormat:@"sfsdfnsdfn"];
NSString *str6 = [[NSString alloc] initWithFormat:@"打算多久但是"];
log:
str1 :  : __NSCFConstantString -> 0x102343500 : 18446744073709551615
str2 : 11 : NSTaggedPointerString -> 0xa000000000031312 : 18446744073709551615
str3 : 2222 : NSTaggedPointerString -> 0xa000000323232324 : 18446744073709551615
str4 : 333333333 : NSTaggedPointerString -> 0xa1f7df7df7df7df9 : 18446744073709551615
str5 : sfsdfnsdfn : NSTaggedPointerString -> 0xa019162a2b62a2ba : 18446744073709551615
str6 : 打算多久但是 : __NSCFString -> 0x60000004ea00 : 1

此时多出了两个类簇 NSTaggedPointerString 和 __NSCFString

NSTaggedPointerString 是伪对象,直接将字符串存在指针的地址里,所以可以直接从地址读取字符串,例如 0xa000000000031312,31是'1'的十六进制的ASCII码,字符串长度为2,关于 NSTaggedPointerString 想深入了解可以去看看: iOS开发-深入理解Tagged Pointer

__NSCFString 根据引用计数可以指定是存放在堆上的,也就是需要我们进行内存管理的对象,根据以上测试来看是优先级最低的类簇,这可能是为了减少数据小却消耗大内存的内存浪费现象,以及提高运算速率。

注1:当字符串中包含ASCII码无法表示的字符时,不会以NSTaggedPointerString来存储字符串,例如中文。

注2: 和 stringWithFormat 与 initWithFormat 结果一样就不再展示。

3、NSMutableString 

NSMutableString *str1 = [NSMutableString stringWithFormat:@"11"];
NSMutableString *str2 = [NSMutableString stringWithFormat:@"2222"];
NSMutableString *str3 = [NSMutableString stringWithFormat:@"333333333"];
NSMutableString *str4 = [[NSMutableString alloc] initWithFormat:@"sfsdfnsdfn"];
NSMutableString *str5 = [[NSMutableString alloc] initWithFormat:@"打算多久但是"];
log:
str1 : 11 : __NSCFString -> 0x608000072e00 : 1
str2 : 2222 : __NSCFString -> 0x608000072980 : 1
str3 : 333333333 : __NSCFString -> 0x608000072380 : 1
str4 : sfsdfnsdfn : __NSCFString -> 0x608000072580 : 1
str5 : 打算多久但是 : __NSCFString -> 0x608000072f00 : 1

可以看到不管 NSMutableString 怎么创建都是__NSCFString。

4、占位类簇

其实还有一种类簇,那就是占位类簇 NSPlaceholderString 和 NSPlaceholderMutableString,占位类簇只在分配内存时过渡使用,一旦对象初始化后就会得到对应的类簇,而且占位类簇只能调用初始化方法,其他方法会报错,所以我们很难看见和使用到。

总结

经过以上探究观察可以知道,其实 NSString 以及 NSMutableString 都是由 NSPlaceholderStringNSPlaceholderMutableString__NSCFConstantStringNSTaggedPointerString__NSCFString 类簇组成的抽象类。

NSPlaceholderString 和 NSPlaceholderMutableString 只在分配内存时过渡使用,只能调用初始化方法。

__NSCFConstantString 是字符串常量,存储在常量区。

NSTaggedPointerString 是伪对象,使用指针地址作为存储,可以存储长度≤11的字符串,ASCII码无法表示的字符无法使用。

__NSCFString 存储在堆上。

猜你喜欢

转载自blog.csdn.net/qq_36557133/article/details/105354155