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