美文网首页
[Objective-C]奇怪的引用计数

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

作者: 猎手Andy | 来源:发表于2018-06-04 10:26 被阅读0次

    一、第一种情况

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

    相关文章

      网友评论

          本文标题:[Objective-C]奇怪的引用计数

          本文链接:https://www.haomeiwen.com/subject/dzjesftx.html