一、第一种情况
1.下面的代码输出结果是啥
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
id obj = [[NSObject alloc]init];
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj)));//1
NSString *s = @"Kickoff";
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(s)));//2
NSString *ss = [NSString stringWithString:s];
long count = CFGetRetainCount((__bridge CFTypeRef)(ss));//3
printf("retain count = %ld\n",count);
}
return 0;
}
2.如果只是看代码,估计很多人都会认为
- 1 处输出 1
- 2 处输出1
-3 处输出2
3.然鹅,结果是
retain count = 1
retain count = 1152921504606846975
retain count = 1152921504606846975
4.clang -rewrite-objc main.m得到main.cpp打开看看
找到
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
id obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj)));
NSString *s = (NSString *)&__NSConstantStringImpl__var_folders_29_dlrxg50s3jq9jhbpzq2fx4z40000gn_T_main_c05a38_mi_0;
printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(s)));
NSString *ss = ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithString:"), (NSString *)s);
long count = CFGetRetainCount((__bridge CFTypeRef)(ss));
printf("retain count = %ld\n",count);
}
return 0;
}
__NSConstantStringImpl__var_folders_29_dlrxg50s3jq9jhbpzq2fx4z40000gn_T_main_c05a38_mi_0 看看是啥
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_29_dlrxg50s3jq9jhbpzq2fx4z40000gn_T_main_c05a38_mi_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"Kickoff",7};
可以看出他是在DATA区域的常量字符串,也就是预编译的时候就确定了,不需要在运行时确定.
猜测它不参与ARC自动释放逻辑,计数值意义不大,因为他在运行期不需要释放。
二、第二种情况
1.代码
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSString *s = [NSMutableString stringWithString:@"Kickoff"];
printf("s retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(s)));//1
NSString *ss = [NSString stringWithString:s];
long count = CFGetRetainCount((__bridge CFTypeRef)(ss));//2
printf("ss retain count = %ld\n",count);
}
return 0;
}
2.输出结果
s retain count = 2
ss retain count = 9223372036854775807
3.clang -rewrite-objc main.m结果
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSString *s = ((NSMutableString * _Nonnull (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)objc_getClass("NSMutableString"), sel_registerName("stringWithString:"), (NSString *)&__NSConstantStringImpl__var_folders_29_dlrxg50s3jq9jhbpzq2fx4z40000gn_T_main_3ff86b_mi_0);
printf("s retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(s)));
NSString *ss = ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithString:"), (NSString *)s);
long count = CFGetRetainCount((__bridge CFTypeRef)(ss));
printf("ss retain count = %ld\n",count);
}
return 0;
}
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_29_dlrxg50s3jq9jhbpzq2fx4z40000gn_T_main_3ff86b_mi_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"Kickoff",7};
后来发现这篇文章说的比较清楚,可以前往查看。
关键的区别是常量字符串当小于10个字符的时候是直接在DATA区分配的(不参与引用计数自动释放逻辑),超过这个数字后开始在堆上分配(引用计数自动释放),应该是基于成本和性能考虑,少量的字符串直接在DATA区比较合适,大量的字符串在堆上合适,因为你不知道需要多大。