TaggedPointer

先解决个问题

首先定义
@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;
}

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

偶尔会出现尾数为不为1的TaggedPointer 不知道什么原因。但源码确实是这么写的。

本文分享自微信公众号 - 老沙课堂(gh_f73a6b772d4f),作者:沙睿

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 数据结构与算法(六) 二叉树遍历

    •任意一个节点的值都大于其左子树的值•任意一个节点的值都小于其右子树的值•他的左右子树也是一颗二叉搜索树•二叉搜索树可以大大提高效率(搜索和添加删除时间复杂度都...

    老沙
  • 数据结构与算法(三)链表

    •插入操作的时候 如果想在角标1添加,要找到角标1的上一个元素。•边界问题 例如首个元素添加 以及最后一个元素添加

    老沙
  • 数据结构与算法(四)栈

    栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作...

    老沙
  • SAP MM Overall Level 审批的采购申请中行项目里的成本中心必须是同一个!

    笔者所在的D项目,正处于第二个sprint 的方案实现阶段,笔者在对自己所做的配置进行测试的时候,发现如下采购申请,系统找不到release strategy...

    会玩SAP的金哥哥
  • 我走的窄道:在Infor ERP LN产品路线上继续前行

    上个月为期3周的美国之行结束回来后继续忙着Infor ERP LN升级的事情,回头想想2005年的秋天,跟这个ERP结识的那个秋天,那个ERP团队,一直还在坚持...

    崔文远TroyCui
  • 从网络虚拟化,看智能网卡发展史

    5G的到来无疑将加速网络虚拟化的进程,在电信领域,网络虚拟化不仅在核心网,也在网络的边缘。但是,仅仅通过软件解决方案不能提供足够的网络可靠性和服务质量,而具有高...

    SDNLAB
  • Go编程基础-基础篇 20

    文件打开之后,就可以从得到的文件对象中读取文件内容。我们在 a.txt 文件中输入 file test 字符串,来验证读取结果。

    未来最可爱的人
  • 纯手工打造Easy支付库

    Anonymous_95975_565
  • 聊聊rsocket load balancer的Ewma

    SMA(Simple Moving Average),即简单移动平均,其公式如下:

    codecraft
  • MyBatis Generator产生的Example类

    or()方法会产生一个新的Criteria对象,添加到oredCriteria中,并返回这个Criteria对象,从而可以链式表达,为其添加Criterion。...

    亦山

扫码关注云+社区

领取腾讯云代金券