[Objective-C]奇怪的引用计数

一、第一种情况

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区比较合适,大量的字符串在堆上合适,因为你不知道需要多大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值