美文网首页
NSTaggedPoint

NSTaggedPoint

作者: 哦小小树 | 来源:发表于2020-04-28 00:47 被阅读0次

    0x01 TaggedPoint由来

    64位开始,iOS引入了Tagged Pointer技术,用于优化小对象的存储。

    • 主要为了解决两个问题:
    1. 内存资源浪费
    2. 访问效率
    • 特点:
    1. 专门用来存储小对象
    2. Tagged Pointer指针的值不再是堆区地址,而是包含真正的值。所以它不会在堆上再开辟空间了。
    3. 内存读取提升3倍,创建比之前快100多倍,销毁速度更快

    0x02 怎么解决内存和效率这两个问题的?

    • 未使用Tagged Pointer
      NSNumber等对象需要动态分配内存、维护引用计数等。
      总共的空间= 指针空间 + 堆中分配的空间
    • 使用Tagged Pointer
      NSNumber等对象,只需要分配一个指针即可,这个指针内部会包含这些数据内容。
      总空间 = 指针空间
      因为不用去用对象的方式管理引用计数,所以省却了 retainrelease操作。
    • NSTaggedPointer支持的类型有哪些呢?
    // objc源码 objc-internal.h中
     // 支持的类型部分
     // 60-bit payloads
     OBJC_TAG_NSAtom            = 0,
     OBJC_TAG_1                 = 1,
     OBJC_TAG_NSString          = 2,
     OBJC_TAG_NSNumber          = 3,
     OBJC_TAG_NSIndexPath       = 4,
     OBJC_TAG_NSManagedObjectID = 5,
     OBJC_TAG_NSDate            = 6,
    

    0x03 TaggedPoint类型判断方案

     #if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__
     // 64-bit Mac - tag bit is LSB
     #   define OBJC_MSB_TAGGED_POINTERS 0
     #else
     // Everything else - tag bit is MSB
     #   define OBJC_MSB_TAGGED_POINTERS 1
     #endif
     
     #if OBJC_MSB_TAGGED_POINTERS
     #   define _OBJC_TAG_MASK (1UL<<63)        // iOS在最高有效位
     #else
     #   define _OBJC_TAG_MASK 1UL              // MACOS在最低有效位
    

    注意:
    之前的版本(objc4-723之前),变量的值直接存储在指针中,很容易的可以读取出来,例如0xb000000000000012 然而现在的版本中(objc4-750之后),苹果对这个指针做了一些编码处理,不能直接看出来是Tagged Pointer

    综上,最标准的判断方案就是直接打印内存地址

    iOS系统下,最高位为1,或者在MAC OS下最低位为1。那么便是NSTaggedPoint类型


    0x04 NSNumber的验证

    NSNumber *num1 = @1;
    NSNumber *num2 = @1234567;
    NSNumber *num3 = @12345678;
    NSNumber *num4 = @1234567890000000000;
    NSNumber *num5 = @13.5;
    
    NSLog(@"%p-%@",num1,num1.class);  // 0xd242f6d243c25a83-__NSCFNumber
    NSLog(@"%p-%@",num2,num2.class); // 0xd242f6d25114dc83-__NSCFNumber
    NSLog(@"%p-%@",num3,num3.class); // 0xd242f6d2ffa31583-__NSCFNumber
    NSLog(@"%p-%@",num4,num4.class); //  0x100461f50-__NSCFNumber
    NSLog(@"%p-%@",num5,num5.class); // 0x100461fb0-__NSCFNumber
    

    通过以上打印,我们可以发现,打印类型均为__NSCFNumber,但实际是吗。我们打印其指针地址可以只有最后两个才是对象类型,前面三个都是NSTaggedPointNumber类型。

    // 当前是在MAC OS下测试,看低位
    (lldb) p/t 0xd242f6d243c25a83
    (unsigned long) $0 = 0b1101001001000010111101101101001001000011110000100101101010000011
    (lldb) p/t 0x100461f50
    (long) $1 = 0b0000000000000000000000000000000100000000010001100001111101010000
    

    0x05 NSString的验证

    // 创建一个常量字符串__NSCFConstantString
    NSString *test1 = @"测试数据";  
    NSString *test2 = [[NSString alloc] initWithString:@"测试数据"];        // 这种现在不推荐使用,推荐直接使用字面量
    
    
    // 存储在堆区
    NSString *test3 = [[NSString alloc] initWithUTF8String:"测试数据"];     // __NSCFString
    NSString *test4 = [NSString stringWithFormat:@"测试:%d",1];           // __NSCFString
    NSString *test5 = [[NSString alloc] initWithFormat:@"测试%@",@"hello"];   // __NSCFString
    
    // 当字面值常量的数字,英文字母字符串的长度小于10的时候会自动生成这种类型。
    // 如果中间有其他特殊字符可能生成__NSCFString类型
    // 内容直接被存放到指针中,当做一种伪对象
    NSString *test6 = [NSString stringWithFormat:@"1"];             // NSTaggedPointerString
    
    // 最大9位
    NSString *test7 = [NSString stringWithUTF8String:"123456789"];      
    NSString *test8 = [NSString stringWithFormat:@"abcdefghi"];
    
    // 超出位数限制
    NSString *test9 = [NSString stringWithUTF8String:"abcdefghij"];  // __NSCFString
    
    

    在处理字符串方面,我们打印可以看到明确的NSTaggedPointerString类名,可以用来判断。

    总结

    1. 通过字面量创建的字符串是个常量
    2. 通过创建对象的方式,如果字符仅为数字或字母,并且总数小于10个会生成NSTaggedPointString,否则生成CFString
    3. 如果创建对象是,字符有其他非字母数字字符,则不那么容易判断几个字符生成NSTaggedPointString

    0x06 知识点延伸

    // Person.h
    @interface Person : NSObject
    @property (nonatomic, copy) NSString *name;
    @end
    
    // main.m
    Person *p = [Person new];
    for (int i = 0; i< 1000;i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            p.name = [NSString stringWithFormat:@"测试%i",i];
        });
    }
    
    NSLog(@"%@",p.name);
    
    

    问:以上代码能否正常执行?如果crash,为什么

    如果将赋值语句改为下面,那在64位机上是否能执行,为什么?:

     p.name = [NSString stringWithFormat:@"%i",i];
    

    参考资料
    https://mikeash.com/pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html
    http://www.cocoachina.com/articles/13449

    相关文章

      网友评论

          本文标题:NSTaggedPoint

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