前言:

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

Tagged Pointer主要为了解决两个问题:

  1. 内存资源浪费,堆区需要额外的开辟空间
  2. 访问效率,每次set/get都需要访问堆区,浪费时间, 而且需要管理堆区对象的声明周期,降低效率

Tagged Pointer特点:

  1. 专门用来存储小对象,比如NSString,NSNumber,NSDate
  2. Tagged Pointer指针的值不再是堆区地址,而是包含真正的值。所以它不会在堆上再开辟空间了,也不需要管理对象的生命周期了。
  3. 内存读取提升3倍,创建比之前快100多倍,销毁速度更快

一、引入Tagged Pointer 前后对比

1、引入前

NSNumber等对象需要动态分配内存、维护引用计数等。 总共的空间= 指针空间 + 堆中分配的空间

2、引入后

NSNumber等对象,只需要分配一个指针即可,这个指针内部会包含这些数据内容。

总空间 = 指针空间

因为不用去用对象的方式管理引用计数,所以省却了 retainrelease操作。

二、Tagged Pointer 原理

number1只有栈上的指针内存;而maxNum不仅有指针内存,在堆中还分配了32字节的内存用于存储该变量的值。通过观察发现,对象的number1number2number3number4都存储在了对应的指针中;而maxNum不同由于数据过大,导致无法 1 个指针 8 个字节的内存根本存不下,而申请了32字节堆内存。

  1. NSString类型的Tagged Pointer指针与基本类型的指针是不一样的,末尾的数字为字符串的长度;
  2. NSString类型的Tagged Pointer指针存储char类型,返回的是ASCII码(该值为16进制的,需要进行十进制转换)

三、如何判断是否使用了 Tagged Pointer 技术

BOOL isTaggedPointer(id pointer) {
return (long)(__bridge void *)pointer & 1;
}

该函数就是调用了isTaggedPointer

四、使用 Tagged Pointer 注意点

​ 我们知道,所有OC对象都有isa指针,而Tagged Pointer并不是真正的对象,它没有isa指针,所以如果你直接访问Tagged Pointerisa成员的话,在编译时将会有警告。

五、面试题

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abcdefghijk"];
});
} dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<1000; i++) {
dispatch_async(queue, ^{
self.name = [NSString stringWithFormat:@"abc"];
});
}

两者运行结果有何不同?

首先看self.name = [NSString stringWithFormat:@"abcdefghijk"];

崩溃,并且崩溃在objc_release的地方。

是什么原因导致崩溃的呢?

我们知道,

self.name = [NSString stringWithFormat:@"abcdefghijk"];

其实是调用了

[self setName:[NSString stringWithFormat:@"abcdefghijk"]];

而setName:的实现是:

- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];//老的释放掉
_name = [name copy];//传入的值copy后赋值给_name
}
}

由于是async异步操作,self.name = [NSString stringWithFormat:@"abcdefghijk"];即[_name release];有可能会被多条线程同时操作。导致,线程n把_name释放掉,线程n+1又要执行_name的释放,从而造成_name已经被释放两次,第二次访问的时候,_name已经释放过,造成坏内存访问。

解决方法一:atomic

@property (copy, atomic) NSString *name;

从而:

- (void)setName:(NSString *)name
{
//加锁操作
if (_name != name) {
[_name release];
_name = [name copy];
}
//解锁操作
}

解决方法二:

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i<1000; i++) {
dispatch_async(queue, ^{
//加锁
self.name = [NSString stringWithFormat:@"abcdefghijk"];
});
//解锁
}

self.name = [NSString stringWithFormat:@"abc"];

为何没有崩溃呢?

从类型可以看出来,

内容多的name类型是__NSCFString

内容少的name类型是NSTaggedPointerString

这就是原因所在。

内容少的name,由于类型是NSTaggedPointerString,在赋值的时候

是直接在指针里面取值,而不需要release操作,因此,不会崩溃

iOS中 Tagged Pointer 技术的更多相关文章

  1. OS X 和iOS 中的多线程技术(上)

    OS X 和iOS 中的多线程技术(上) 本文梳理了OS X 和iOS 系统中提供的多线程技术.并且对这些技术的使用给出了一些实用的建议. 多线程的目的:通过并发执行提高 CPU 的使用效率,进而提供 ...

  2. OS X 和iOS 中的多线程技术(下)

    OS X 和iOS 中的多线程技术(下) 上篇文章中介绍了 pthread 和 NSThread 两种多线程的方式,本文将继续介绍 GCD 和 NSOperation 这两种方式.. 1.GCD 1. ...

  3. iOS中 动态热修补技术JSPatch 韩俊强的博客

    .1.4) JSPatch bridge Objective-C and JavaScript. You can call any Objective-C class and method in Ja ...

  4. iOS中 图文混排/自定义图文混排 作者:韩俊强

    指示根视图:(准备几张图片,把label加载在window上) CustomLable *label = [[CustomLable alloc]initWithFrame:CGRectMake(0, ...

  5. iOS中常用的四种数据持久化技术

    iOS中的数据持久化方式,基本上有以下四种:属性列表 对象归档 SQLite3和Core Data 1.属性列表涉及到的主要类:NSUserDefaults,一般 [NSUserDefaults st ...

  6. Tagged Pointer

    前言 在2013年9月,苹果推出了iPhone5s,与此同时,iPhone5s配备了首个采用64位架构的A7双核处理器,为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念.对于6 ...

  7. 从一道网易面试题浅谈 Tagged Pointer - darcy_tang 的博客

    前言 这篇博客九月就想写了,因为赶项目拖了到现在,抓住17年尾巴写吧~ 正文 上次看了一篇 <从一道网易面试题浅谈OC线程安全> 的博客,主要内容是: 作者去网易面试,面试官出了一道面试题 ...

  8. iOS 中的 HotFix 方案总结详解

    相信HotFix大家应该都很熟悉了,今天主要对于最近调研的一些方案做一些总结.iOS中的HotFix方案大致可以分为四种: WaxPatch(Alibaba) Dynamic Framework(Ap ...

  9. ios中@class和 #import区别

    很多刚开始学习iOS开发的同学可能在看别人的代码的时候会发现有部分#import操作写在m文件中,而h文件仅仅使用@class进行声明,不禁纳闷起来,为什么不直接把#import放到h文件中呢?这是因 ...

随机推荐

  1. 手脱FSG(2.0)

    1.查壳 2.x32dbg脱壳 在第二个xchg处使用ESP定律脱壳: 由于FSG壳特性,在跳转后位置向上查找,找到js\jne\jmp,jmp就是OEP位置: 在此处使用工具进行脱壳: 完成! 3. ...

  2. Python 散列表查询_进入<哈希函数>为结界的世界

    1. 前言 哈希表或称为散列表,是一种常见的.使用频率非常高的数据存储方案. 哈希表属于抽象数据结构,需要开发者按哈希表数据结构的存储要求进行 API 定制,对于大部分高级语言而言,都会提供已经实现好 ...

  3. 手动验证 TLS 证书

    证书结构 我们现在使用的 TLS 证书的标准是 X.509,版本号为 V3.版本号可从证书的 Version 字段看到. 根据 RFC 3280 定义的证书结构,证书由三个部分组成: 证书主体(TBS ...

  4. 有了 Promise 和 then,为什么还要使用 async?

    有了 Promise 和 then,为什么还要使用 async? 本文写于 2020 年 5 月 13 日 最近代码写着写着,我突然意识到一个问题--我们既然已经有了 Promise 和 then,为 ...

  5. python 批量探测服务端开放的TCP端口

    现在大多服务器都有做icmp限制或直接禁掉,导致我们业务去连接服务器异常时无法判断是程序问题还是网络问题,所以写一个简单探测tcp端口脚本来探测服务器所开放的端口,再使用tcp测试双向时延来排掉网络问 ...

  6. SylixOS——虚拟机网络配置

    网络配置 点击设置按钮 新建虚拟网络适配器 输入IP地址(注意:IP地址必须和SylixOS在同一个子网内) 点击确定等待,虚拟网络适配器建立完成后效果如下(多了一个名为"以太网2" ...

  7. 干掉Switch-Case、If-Else----订阅发布模式+事件驱动模式

    在上位机和下位机或者服务端和客户端通信的时候,很多时候可能为了赶项目进度或者写代码方便直接使用Socket通信,传输string类型的关键字驱动对应的事件,这就有可能导致程序中存在大量的Switch- ...

  8. Fail2ban 使用Fail2ban监禁SSH服务的恶意IP

    Fail2ban自带了很多服务的过滤器(filter)和动作(action),它已经帮你做好了,所以一般情况下我们无需定义,直接引用即可. 这边只是一个示例. 系统版本:Ubuntu 16.04.5 ...

  9. 【摸鱼神器】一次搞定 vue3的 路由 + 菜单 + tabs

    做一个管理后台,首先要设置路由,然后配置菜单(有时候还需要导航),再来一个动态tabs,最后加上权限判断. 这个是不是有点繁琐?尤其是路由的设置和菜单的配置,是不是很雷同?那么能不能简单一点呢?如果可 ...

  10. MathType7安装使用及please restart word to load mathtype addin properly的问题

    MathType7安装使用及please restart word to load mathtype addin properly的问题.最近在自己的电脑上安装Mathtype7,把遇到的问题和解决办 ...