Tagged Pointer

1,336 阅读2分钟

先解决个问题

首先定义
@property (nonatomic, copy) NSString *test;
方法一
  for (int i = 0; i < 1000; i++) {
      dispatch_async(dispatch_get_global_queue(0, 0), ^{
          self.test = [NSString stringWithFormat:@"%@",@"123"];
      });
  }

方法二
  for (int i = 0; i < 1000; i++) {
      dispatch_async(dispatch_get_global_queue(0, 0), ^{
          self.test = [NSString stringWithFormat:@"%@",@"abababababababababababababab"];
      });
  }

运行段代码 有什么区别? 现象是什么?

  • 方法一:正常运行
  • 方法二:崩溃

为什么?

查看崩溃日志

  • 坏内存访问

分析原因

test属性 setter方法实际执行以下内容

- (void)setTest:(NSString *)test {
    if (![_test isEqualToString:test]) {
        [_test release];
        _test = [test copy];
        [test release];
    }
}

由于test 修饰为nonatomic 所以是线程不安全的。 当多条线程同时访问,造成多次release ,所以坏内存访问。

解决方式

修饰改为atomic 或者加锁

疑问

为什么方式一不会崩溃?

首先打印两个NSString的类型

解决疑问

正常对象都是 指针指向对象的地址, 指针指向堆内存中的地址,所以方法二会因为多线程访问而造成坏内存访问,而TaggedPointer 则不会创建内存,而是在isa指针上做手脚。在指针上存放具体值。

TaggedPointer

64位开始 引入了Tagged Pointer 技术,用于优化NSNumber、NSDate、NSString 等小对象存储

打印方式一、方式二的NSString地址

从上图可以看出 0结尾的为对象地址 因为以16位为基准 内存对齐

而方法二的明显不一样。

我们看一下objc_release的源码
objc_release(id obj)
{
    if (!obj) return;
    if (obj->isTaggedPointer()) return;
    return obj->release();
}

当obj为isTaggedPointer的时候 直接返回。 所以更加验证了刚才的说法 即:用指针存值,而不是在堆中生成对象

objc_object::isTaggedPointer() 
{
    return _objc_isTaggedPointer(this);
}
#   define _OBJC_TAG_MASK 1UL
static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
#if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__ //如果是OSX && X86 
    // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0 
#else  //其他情况 包含iOS
    // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif


#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)  //如果是OSX && X86 

#else
#   define _OBJC_TAG_MASK 1UL  //其他情况 包含iOS

#endif

从上面可以看当在OSX && X86 出当1UL<<63为1的时候为TaggedPointer

从上面可以看当在iOS平台 出当尾数为1的时候为TaggedPointer

感兴趣的可以关注我的公众号。每天会更新哦 非常感谢。相互交流 提升技术~