天天看点

Tagged Pointer遐想一、NSString二、NSNumber三、 线程安全四、objc_msgSendTodo:摘要

Tagged Pointer遐想

  • 一、NSString
    • __NSCFConstantString
    • NSTaggedPointerString
    • __NSCFString
    • copy
    • mutableCopy
    • 中文或者特殊字符(非ASCII字符)
    • autorelease
    • NSMutableString
    • string
  • 二、NSNumber
  • 三、 线程安全
  • 四、objc_msgSend
  • Todo:
  • 摘要

Tagged Pointer(64bit系统对 NSString、NSNumber 和 NSDate等对象进行优化的一种方式)

一、NSString

类名 存储区域 初始化的引用计数 作用描述
NSString 堆区 1 开发者常用的不可变字符串类,编译期间会转换到其他类型
NSMutableString 堆区 1 开发者常用的可变字符串类,编译期间会转换到其他类型
__NSCFString 堆区 1 可变字符串NSMutableString类,编译期间会转换为该类型
__NSCFConstantString 堆区 264-1 不可变字符串NSString类,编译期间会转换为该类型
NSTaggedPointerString 堆区 264-1 Tagged Pointer对象,并不是真的对象

__NSCFConstantString

  • __NSCFConstantString
//    地址相同。类型为__NSCFConstantString
    NSString *kNSCFConstantString = @"abc”;
    NSString *kNSCFConstantString1 = @"abc";
    NSString *kNSCFConstantString_initWithString1 = [[NSString alloc] initWithString:@"abc"];
    NSString *kNSCFConstantString_initWithString2 = [[NSString alloc] initWithString:kNSCFConstantString];
    NSString *kNSCFConstantString_stringWithString1 = [NSString stringWithString:@"abc"];
    NSString *kNSCFConstantString_stringWithString2 = [NSString stringWithString:kNSCFConstantString];
           
  • 这两个地址相同
NSLog(@"%d",kNSCFConstantString == @"abc”);
           
  • 这两个地址不相
po kNSCFConstantString == @"abc"
           

因为

p @"abc"
(NSTaggedPointerString *) $2 = 0xbe850bfb6b5f72de @“abc"
           

NSTaggedPointerString

边界9个,且都为ASCII字符

NSString *kNSCFConstantString = @"abc";//__NSCFConstantString
    //    地址相同。类型为NSTaggedPointerString
    NSString *kNSTaggedPointerString_initWithFormat = [[NSString alloc] initWithFormat:kNSCFConstantString];
    NSString *kNSTaggedPointerString_stringWithFormat = [NSString stringWithFormat:kNSCFConstantString];
           

__NSCFString

NSString *kNSCFConstantString = @"abcabcabca";//__NSCFConstantString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString];
           

copy

NSString *kNSCFConstantString1 = @"abc";//__NSCFConstantString
    NSString *kNSCFConstantString2 = @"abcabcabca";//__NSCFConstantString
    NSString *kNSTaggedPointerString = [NSString stringWithFormat:kNSCFConstantString1];//NSTaggedPointerString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString2];//__NSCFString
    // 地址和kNSCFConstantString1相同,类型为__NSCFConstantString
    NSString *kNSCFConstantString_copy = [kNSCFConstantString1 copy];
    // 地址和kNSTaggedPointerString相同,类型为NSTaggedPointerString
    NSString *kNSTaggedPointerString_copy = [kNSTaggedPointerString copy];
    // 地址和kNSCFString相同,类型为__NSCFString
    NSString *kNSCFString_copy = [kNSCFString copy];
           

mutableCopy

NSString *kNSCFConstantString1 = @"abc";//__NSCFConstantString
    NSString *kNSCFConstantString2 = @"abcabcabca";//__NSCFConstantString
    NSString *kNSTaggedPointerString = [NSString stringWithFormat:kNSCFConstantString1];//NSTaggedPointerString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString2];//__NSCFString
    
//    地址变化,类型都为__NSCFString
    NSMutableString *kNSCFConstantString_mutableCopy = [kNSCFConstantString1 mutableCopy];
    NSMutableString *kNSTaggedPointerString_mutableCopy = [kNSTaggedPointerString mutableCopy];
    NSMutableString *kNSCFString_mutableCopy = [kNSCFString mutableCopy];
           

中文或者特殊字符(非ASCII字符)

NSString *kNSCFConstantString1 = @"中文";//__NSCFConstantString
    NSString *kNSCFString = [NSString stringWithFormat:kNSCFConstantString1];//__NSCFString
           

autorelease

autorelease的时候发现如果是Tagged Pointer是不会autorelease的

NSMutableString

NSString *kNSCFConstantString1 = @"abc";//__NSCFConstantString
//类型都是__NSCFString
    NSMutableString *kNSMutableString1 = [[NSMutableString alloc] initWithString:kNSCFConstantString1];
    NSMutableString *kNSMutableString2 = [NSMutableString stringWithString:kNSCFConstantString1];
    NSMutableString *kNSMutableString3 = [[NSMutableString alloc] initWithFormat:kNSCFConstantString1];
    NSMutableString *kNSMutableString4 = [NSMutableString stringWithFormat:kNSCFConstantString1];
    
    NSMutableString *kNSMutableString5 = [[NSMutableString alloc] initWithCapacity:0];
    NSMutableString *kNSMutableString6 = [[NSMutableString alloc] initWithCapacity:1];
    NSMutableString *kNSMutableString7 = [NSMutableString stringWithCapacity:0];
    NSMutableString *kNSMutableString8 = [NSMutableString stringWithCapacity:1];
    NSMutableString *kNSMutableString9 = [NSMutableString string];

    NSString *kNSMutableString_copy = [kNSMutableString1 copy];//NSTaggedPointerString
    NSMutableString *kNSMutableString_mutable = [kNSMutableString1 mutableCopy];//__NSCFString
           

string

//    类型都是__NSCFConstantString,值为@""
    //    kNSString1和kNSString3地址相同,与kNSString2不同
    NSString *kNSString1 = [NSString string];
    NSString *kNSString2 = @"";
    NSString *kNSString3 = [NSString string];
           

二、NSNumber

类名 存储区域 初始化的引用计数 作用描述
NSValue 堆区 1 开发者常用的不可变字符串类,编译期间会转换到其他类型
NSNumber 堆区 1 开发者常用的可变字符串类,编译期间会转换到其他类型
__NSCFNumber 堆区、栈区 1、264-1 可变字符串NSMutableString类,编译期间会转换为该类型
//    NSNumber没有mutableCopy
    NSNumber *number1 = @1;
    NSNumber *number2 = @2;
    NSNumber *number3 = @3;
    NSNumber *number4 = @(3.14159265);
    //    copy不改变地址
    NSNumber *number5 = [number1 copy];
    NSNumber *number6 = [number4 copy];
           

NSNumber看起来都是__NSCFNumber类型。

实际存储会通过Tagged Pointer方式存小数字,并且遵循栈区+max retain count的原则。

三、 线程安全

@property (nonatomic, copy) NSString *testStr;
{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for(int i=0; i<10000; i++) {
        dispatch_async(queue, ^{
            self.testStr = [NSString stringWithFormat:@"12666666666666666663"];
        });
    }
}
           

这段代码执行的话会出现什么事情呢?testStr是nonatomic的,在多个线程同时访问的时候会出现BAD_ACCESS问题,当然你可以把testStr改成atomic来解决这个问题或者用串行queue。

一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。

但是如果你将12666666666666666663改成123,那么你会发现不会crash了,这是为什么呢?

因为当string不长的时候,其实它是一个Tagged Pointer,那么它其实没有堆区对象,当我们赋值的时候就是改下指针(因为指针里存了值),所以其实一次读写就能完成,不用担心线程安全的问题。

无法一次读写完成的例子如:

比如32位的系统中:

一个字节等于8位

一个Bool值占1字节,可以在一次读写操作中完成赋值或取值,因此是线程安全的。

一个指针占4字节,也可以在一次读写操作中完成赋值或取值,因此是线程安全的。

一个double占8字节,则需要两次读写操作才能完成赋值或取值,因此就会存在一个写操作(需两次写操作才能完成),之后就是读操作的可能,导致异常值的现象。

四、objc_msgSend

在进行方法调用时,objc_msgSend会进行识别,不会从isa查找cache、父类查找、消息转发的过程了,直接从指针中取出值进行操作。

可以节省内存,使用上也优化了,不需要经历消息发送这类的过程了。

需要注意,如果一个对象是TaggedPointer,就不存在isa指针了,不算真正的OC对象,只是看似对象的普通变量而已。要避免设计对isa的操作。

换成相应的方法调用如 isKindOfClass 和 object_getClass,只要避免在代码中直接访问对象的isa变量,即可避免这个问题。因为现在已经不允许直接用isa,强制要求object_getClass,而object_getClass已经对tagged pointer进行特殊处理。

Todo:

NSDate

摘要

https://mp.weixin.qq.com/s/1otKcoLgByC0zPBVkp_uhg