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

2018-06-04  本文已影响0人  猎手Andy

一、第一种情况

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.如果只是看代码,估计很多人都会认为

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

上一篇 下一篇

猜你喜欢

热点阅读