Xcode到底对NSString做了什么优化?
首先看个例子: 一般在OC中都是在堆中开辟一块内存存储对象, 但是NSString是有点区别的.
NSObject *obj1 = [[NSObject alloc]init]; //<NSObject: 0x100539310>
NSObject *obj2 = [[NSObject alloc]init]; //<NSObject: 0x100538a90>
结论:
- NSString对象如果通过字面量创建的话, 是存储在常量区的, 并且字面量相同的话, 是共占一份内存的
- NSString对象如果通过
stringWithString
或者initWithString
创建的, 值依然 是存储在常量区的, 并且值相同的话, 是共占一份内存的. 并且如果通过这两种方式创建字符串, 编译器会告诉你这是多余的 Using ‘stringWithString:’ with a literal is redundant 或者 Using ‘initWithString:’ with a literal is redundant, 建议你用字面量替换. - NSString对象如果通过
stringWithFormat
或者initWithFormat
创建的, 对象是存储的堆区的, 如果字符串的值相同, 也是共占一份内存的.
为了探究Xcode到底对NSString做了什么优化, 分别使用字面量, 类方法, 对象方法创建3个字符串, 看看地址, 引用计数器,和值
// 1. 字面量
NSString *s1 = @"string1";
NSString *s1_1 = @"string1";
// 2. 类方法, 创建一个字符串的类方法有2种
NSString *s4 = [NSString stringWithString:@"string1"];
NSString *s5 = [NSString stringWithFormat:@"%@", @"string1"];
// 辅助证明
NSString *s6 = [NSString stringWithString:@"string1"];
NSString *s7 = [NSString stringWithFormat:@"%@", @"string1"];
// 3 对象方法 创建一个字符串的对象方法常用的也是2种
NSString *s10 = [[NSString alloc]initWithString:@"string1"];
NSString *s11 = [[NSString alloc]initWithFormat:@"string1"];
// 辅助证明
NSString *s12 = [[NSString alloc]initWithString:@"string1"];
NSString *s13 = [[NSString alloc]initWithFormat:@"string1"];
// 打印 地址, 引用计数器, 值
// 先打印部分对象
NSLog(@"s1: %p, %ld, %@", s1,(long)CFGetRetainCount(CFBridgingRetain(s1)),s1);
NSLog(@"s1_1: %p, %ld, %@", s1_1,(long)CFGetRetainCount(CFBridgingRetain(s1_1)),s1_1);
NSLog(@"s4: %p, %ld, %@", s4,(long)CFGetRetainCount(CFBridgingRetain(s4)),s4);
NSLog(@"s5: %p, %ld, %@", s5,(long)CFGetRetainCount(CFBridgingRetain(s5)),s5);
NSLog(@"s10: %p, %ld, %@", s10,(long)CFGetRetainCount(CFBridgingRetain(s10)),s10);
NSLog(@"s11 %p, %ld, %@", s11,(long)CFGetRetainCount(CFBridgingRetain(s11)),s11);
printf("\n------------------------分割线------------------------------\n");
// 打印辅助对象的地址, 计数器, 值
NSLog(@"s6: %p, %ld, %@", s6,(long)CFGetRetainCount(CFBridgingRetain(s6)),s6);
NSLog(@"s7: %p, %ld, %@", s7,(long)CFGetRetainCount(CFBridgingRetain(s7)),s7);
NSLog(@"s12: %p, %ld, %@", s12,(long)CFGetRetainCount(CFBridgingRetain(s12)),s12);
NSLog(@"s13: %p, %ld, %@", s13,(long)CFGetRetainCount(CFBridgingRetain(s13)),s13);
// 打印结果
2021-07-04 15:46:02.091105+0800 oc[13313:1719173] s1: 0x100004018, 1152921504606846975, string1
2021-07-04 15:46:02.091569+0800 oc[13313:1719173] s1_1: 0x100004018, 1152921504606846975, string1
2021-07-04 15:46:02.091655+0800 oc[13313:1719173] s4: 0x100004018, 1152921504606846975, string1
2021-07-04 15:46:02.091759+0800 oc[13313:1719173] s5: 0xe9c13f44bd0a3a69, 9223372036854775807, string1
2021-07-04 15:46:02.091813+0800 oc[13313:1719173] s10: 0x100004018, 1152921504606846975, string1
2021-07-04 15:46:02.091873+0800 oc[13313:1719173] s11 0xe9c13f44bd0a3a69, 9223372036854775807, string1
------------------------分割线------------------------------
2021-07-04 15:46:02.091935+0800 oc[13313:1719173] s6: 0x100004018, 1152921504606846975, string1
2021-07-04 15:46:02.091987+0800 oc[13313:1719173] s7: 0xe9c13f44bd0a3a69, 9223372036854775807, string1
2021-07-04 15:46:02.092024+0800 oc[13313:1719173] s12: 0x100004018, 1152921504606846975, string1
2021-07-04 15:46:02.092062+0800 oc[13313:1719173] s13: 0xe9c13f44bd0a3a69, 9223372036854775807, string1
- 可以看到s1与s1_1 是用相同字面量创建的2个不同的对象, 但是他们的内存地址, 计数器, 值都是相同的, 可以证明结论1
- s4和s10是分别通过类方法和对象方法用一个字面量来创建的2个不同的对象, 可以看到他们的内存地址, 计数器, 值都是相同的, 并且与用字面量创建的s1的值是一样的, 可以证明结论2
- s5和s11是分别通过带format的类方法和带format的对象方法创建的2个对象, 他们的内存地址, 计数器, 值都是相同的, 但是与之前用字面量创建的字符串地址是不同的, 说明他们存在堆区, 并且他们两个共占一份内存, 证明结论3
- s6是通过stringWithString创建的新的对象, 它与字面量创建的对象完全一致, 进一步验证了结论
- s13是通过initWithFormat创建的一个新的对象, 它与s5完全一致, 进一步辅助验证了结论.