一个错误例子

@property (nonatomic, strong) NSString *string;dispatch_queue_t queue = dispatch_queue_create("memoryBeingFreedCase_4", DISPATCH_QUEUE_CONCURRENT);for (int i = 0; i < 1000000; i++) {dispatch_async(queue, ^{self.string = [NSString stringWithFormat:@"The num is %d", i];});
}

当运行后, 就会崩溃在给self.string的一行, 错误log如下:

malloc: *** error for object 0x600000639480: Non-aligned pointer being freed (2)

错误原因

来看下原因是为何? 其实是因为setter方法中, 对strong修饰的属性会有一个retain和release的操作。 在并发多线程中的赋值操作中, 都是对_string指针进行的操作, 可能在_string刚刚被release后进行第3行代码的赋值操作。这时_string指向的内存地址是已经被释放了, 所以造成上面的错误。

- (void)setString:(NSString *)string {[string retain];  //1[_stirng release];//2_string = string; //3
}

解决方案

1.将并发执行的任务改为串行执行。
2.将属性开启atomic原子特性。
3.利用Tagged Pointer特性。

前两个方案的具体方法就不絮述了, 只说第三个方案。如果将上面的例子中, 改动一行代码, 重新运行。

self.string = [NSString stringWithFormat:@"%d", i];

这时, 你会发现, 竟然没有问题了, 这究竟为什么呢, 到底什么这么神奇呢? 下面直接引入本文主题Tagged Pointer。

Tagged Pointer

2013年9月, 苹果发布iPhone5s, 其搭载了苹果A7处理器, 是首个采用64位架构的处理器。关于iPhone系列的处理器指令集可以参阅之前写过一篇关于Architectures与指令集架构的博客。也是从采用64位处理器后, 为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。对于64位程序,引入Tagged Pointer后,相关逻辑能减少一半的内存占用,以及3倍的访问速度提升,100倍的创建、销毁速度提升。

Tagged Pointer 之前

比如, NSInteger类型的变量,它所占用的内存是与处理器的位数有关,在32位CPU下占4个字节,在64位CPU下是占8个字节的。而指针类型的大小通常也是与CPU位数相关,一个指针所占用的内存在32位CPU下为4个字节,在64位CPU下也是8个字节。
所以在没有Tagged Pointer对象之前,从32位机器迁移到64位机器中后,虽然逻辑没有任何变化,但这种NSNumber、NSDate一类的对象所占用的内存会翻倍。而且从效率上来说,我们需要在堆上为其分配内存,另外还要维护它的引用计数,管理它的生命期。这些都给程序增加了额外的逻辑,造成运行效率上的损失。

Tagged Pointer 之后

为了改进上面提到的内存占用和效率问题,苹果提出了Tagged Pointer对象。由于NSNumber、NSDate一类的变量本身的值需要占用的内存大小常常不需要8个字节。我们可以将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。所以在这总共8字节的内存中, 把标记位除去后, 其他的内存大小都可以存储数据。所以,引入了Tagged Pointer对象之后,其在64位处理器下的内存图变成了以下这样:

而且, 如果你所要存储的数据大小超出Tagged Pointer对象可存储大小的话, 系统将不会以Tagged Pointer的方式, 将会以普通对象的方式来保存。所以, 这个优化并不需要人为的干预。

代码验证

    NSMutableString *string = [NSMutableString stringWithString:@"1"];for(int i = 0; i < 20; i++){NSNumber *number = @([string longLongValue]);NSLog(@"%@: %p---%p", [number class], number, &number);[string appendString:@"1"];}

以NSNumber类型举例, 打印结果:

__NSCFNumber: 0xb000000000000013---0x7ffee2f43698
__NSCFNumber: 0xb0000000000000b3---0x7ffee2f43698
__NSCFNumber: 0xb0000000000006f3---0x7ffee2f43698
__NSCFNumber: 0xb000000000004573---0x7ffee2f43698
__NSCFNumber: 0xb00000000002b673---0x7ffee2f43698
__NSCFNumber: 0xb0000000001b2073---0x7ffee2f43698
__NSCFNumber: 0xb0000000010f4473---0x7ffee2f43698
__NSCFNumber: 0xb00000000a98ac73---0x7ffee2f43698
__NSCFNumber: 0xb000000069f6bc73---0x7ffee2f43698
__NSCFNumber: 0xb000000423a35c73---0x7ffee2f43698
__NSCFNumber: 0xb000002964619c73---0x7ffee2f43698
__NSCFNumber: 0xb000019debd01c73---0x7ffee2f43698
__NSCFNumber: 0xb000102b36211c73---0x7ffee2f43698
__NSCFNumber: 0xb000a1b01d4b1c73---0x7ffee2f43698
__NSCFNumber: 0xb00650e124ef1c73---0x7ffee2f43698
__NSCFNumber: 0xb03f28cb71571c73---0x7ffee2f43698
__NSCFNumber: 0xb27797f26d671c73---0x7ffee2f43698
__NSCFNumber: 0x6000006297c0---0x7ffee2f43698
__NSCFNumber: 0x6000006297c0---0x7ffee2f43698
__NSCFNumber: 0x6000006297c0---0x7ffee2f43698

通过结果分析, 在打印地址中除去最后的数字最末尾的3以及最开头的0xb, 其它数字刚好表示了相应NSNumber的值。可见,苹果确实是将值直接存储到了指针本身里面。也可能数字最末尾的3以及最开头的0xb就是苹果对于Tagged Pointer的特殊标记。在最后的三行打印结果中,由于Tagged Pointer无法将其按上面的压缩方式来保存,那么应该就会以普通对象的方式来保存, 所以打印的结果是栈区和堆区的内存地址。

    NSMutableString *string2 = [NSMutableString stringWithString:@"1"];for( int i = 0; i < 14; i++){NSString *strFor = [[string2 mutableCopy] copy];NSLog(@"%@: %p---%p", [strFor class], strFor, &strFor);[string2 appendString:@"1"];}

以NSString类型举例, 打印结果:

NSTaggedPointerString: 0xa000000000000311---0x7ffee9e64698
NSTaggedPointerString: 0xa000000000031312---0x7ffee9e64698
NSTaggedPointerString: 0xa000000003131313---0x7ffee9e64698
NSTaggedPointerString: 0xa000000313131314---0x7ffee9e64698
NSTaggedPointerString: 0xa000031313131315---0x7ffee9e64698
NSTaggedPointerString: 0xa003131313131316---0x7ffee9e64698
NSTaggedPointerString: 0xa313131313131317---0x7ffee9e64698
NSTaggedPointerString: 0xa0079e79e79e79e8---0x7ffee9e64698
NSTaggedPointerString: 0xa1e79e79e79e79e9---0x7ffee9e64698
NSTaggedPointerString: 0xa03def7bdef7bdea---0x7ffee9e64698
NSTaggedPointerString: 0xa7bdef7bdef7bdeb---0x7ffee9e64698
__NSCFString: 0x60400042f560---0x7ffee9e64698
__NSCFString: 0x600000437060---0x7ffee9e64698
__NSCFString: 0x600000436e80---0x7ffee9e64698

这个例子中, 前面的部分类型打印出来都是NSTaggedPointerString, 这就很明显了。也可能开头的0xa就是苹果对于Tagged Pointer的特殊标记。在最后的三行打印结果中,由于Tagged Pointer无法将其按上面的压缩方式来保存,那么应该就会以普通对象的方式来保存, 所有后面的类型也就变为__NSCFString了。

再看下我在控制台进行的一些打印:

这个打印结果, 足以说明它是一个特别的指针,且不指向任何一个地址。所有对象都有 isa 指针,而Tagged Pointer其实是没有的,因为它不是真正的对象。 所以如果你直接访问Tagged Pointer的isa成员的话,在编译时将会有警告。

特点

1.Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free。
2.在内存读取上有着3倍的效率,创建时比以前快106倍。不但减少了64位机器下程序的内存占用,还提高了运行效率。完美地解决了小内存对象在存储和访问效率上的问题。
3.这是一个特别的指针,不指向任何一个地址。
4.Tagged Pointer没有isa指针, 所以其不是真正的对象。

总结

还是得引用唐巧文章中的原话, 苹果将Tagged Pointer引入,给64位系统带来了内存的节省和运行效率的提高。Tagged Pointer通过在其最后一个bit位设置一个特殊标记,用于将数据直接保存在指针本身中。因为Tagged Pointer并不是真正的对象,我们在使用时需要注意不要直接访问其isa变量。

参考文章:
深入理解Tagged Pointer
Let’s Build Tagged Pointers
Tagged Pointer Strings

从一个例子来看Tagged Pointer特性相关推荐

  1. tagged pointer

    产生背景 在项目开发中,我们总是希望能够节约空间,减少不必要的系统开销,或者提升效率,缩短执行时间,当然有时候也会通过增加空间开销来换取执行时间的缩减.可以为了方便CPU寻址提升存取效率,多数情况下内 ...

  2. 生成句法分析树以及从一个小例子来看词义消歧及语义角色标注

    一.生成句法分析树 把一句话按照句法逻辑组织成一棵树,由人来做这件事是可行的,但是由机器来实现是不可思议的,然而算法世界就是这么神奇,把一个十分复杂的过程抽象成仅仅几步操作,甚至不足10行代码,就能让 ...

  3. Tagged Pointer分析

    在objc4源码中,我们经常会在函数中看到Tagged Pointer.Tagged Pointer究竟是何方神圣? 从64位系统开始,iOS引入了Tagged Pointer技术,用于优化小对象(N ...

  4. macOS 与 iOS 中的 Tagged Pointer

    目录 Tagged Pointer 的简介 解除 Tagged Pointer 的数据混淆 Tagged Pointer 的原理:macOS Tagged Pointer 的原理: iOS Tagge ...

  5. 3-runtime 之 Tagged Pointer

    Tagged Pointer 是自从iPhone 5s 之后引入的特性 1 先说一下iOS的内存布局 代码区:存放编译之后的代码 数据段 :字符串常量 : NSString *hello = @&qu ...

  6. 内存管理之Tagged pointer

    iOS开发者对引用计数这个名词肯定不陌生,引用计数是苹果为了方便开发者管理内存而引入的一个概念,当引用计数为0时,对象就会被释放.但是,真的是所有对象都是这样吗? 内存分配 iOS将虚拟内存按照地址由 ...

  7. Tagged Pointer遐想

    Tagged Pointer遐想 一.NSString __NSCFConstantString NSTaggedPointerString __NSCFString copy mutableCopy ...

  8. NSNumber 与 Tagged Pointer

    TL.DR 本文以NSNumber为例,说明一个Tagged Pointer是怎样被创建出来的. 从代码开始 没有isa int main(int argc, const char * argv[]) ...

  9. iOS Tagged Pointer

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

  10. 采用Tagged Pointer的字符串

    本文由CocoaChina译者@ALEX吴浩文翻译 作者:Mike Ash 原文:Friday Q&A 2015-07-31: Tagged Pointer Strings 翻译原文:采用Ta ...

最新文章

  1. 不挂载 组件渲染_让你的 React 组件性能跑得再快一点「实践」
  2. 监控软件nagios之安装
  3. mysql获取当前时间,前一天,后一天(执行效率比较高)
  4. 苹果cms对接影视小程序源码完整源码
  5. Tcl Tutorial 笔记1 · 输出
  6. 95.Extjs 表单中自定义的验证规则 VTypes
  7. mysql查看授权_mysql查看用户授权信息的具体方法
  8. Python|十五个超级炫酷的代码
  9. VirtualBox6.1安装及使用教程
  10. (精)广东工业大学 2018实时大数据分析——A-Priori算法实验报告
  11. larval 策略模式
  12. Firefox OS 架构简析——(1)
  13. kindle_Kindle颂歌
  14. 嵌入式开发(一):嵌入式开发新手入门(转载后续需要修改补充)
  15. openwrt的UCI 网络配置与Luci在线安装
  16. 基于HTML和css的静态网页仿写
  17. 【九章算法】-动态规划
  18. 『无为则无心』Python函数 — 32、递归
  19. 树莓派最新版系统烧写和网络配置
  20. uniapp小程序使用省市县区选择器picker

热门文章

  1. 显示器固件测试软件,【安全工具】固件可视化分析-工具篇
  2. matlab 模拟光源,基于 Matlab 的激光光斑模拟.pdf
  3. Mybatis源码-cursor(游标)
  4. 互联网+房地产最后拼什么?除了技术还是技术
  5. 伪随机数产生原理及用法
  6. win10开机黑屏不显示桌面,怎么办
  7. 4.7/4.8 磁盘挂载
  8. 计算机配置里面没有网络选项,解决win10网络设置中没有以太网的方法
  9. 第九届河南省程序设计大赛-NYOJ-1276(搜索batter)
  10. Android启动优化