内容简介:最近和基友 Maize 聊天,他给我普及了一个有意思的知识点,回看唐巧的 深入理解Tagged Pointer 的文章,再结合之前在公司看到的代码,突然有了一些灵感,我们先上一段代码。如果此时我们给 someProperty 属性赋值并打印该属性的值,你认为它会 crash 么? 我们先说一下结论,如果这个属性的值为100,那么它不会 crash,如果是 100.1 那么就一定会 crash。 如果你马上就明白了其中的原理,那么恭喜你,你完全不用再花时间看此篇文章了。 如果你没有意识到其中的问题,不妨花上
最近和基友 Maize 聊天,他给我普及了一个有意思的知识点,回看唐巧的 深入理解Tagged Pointer 的文章,再结合之前在公司看到的代码,突然有了一些灵感,我们先上一段代码。
@interface NSObject (AssociatedObject) @property (nonatomic, assign) CGFloat someProperty; @end @implementation NSObject (AssociatedObject) @dynamic someProperty; - (void)setSomeProperty:(CGFloat)someProperty{ return objc_setAssociatedObject(self, @selector(someProperty), @(someProperty), OBJC_ASSOCIATION_ASSIGN); } - (CGFloat)someProperty{ return [objc_getAssociatedObject(self, @selector(someProperty)) floatValue]; } @end 复制代码
如果此时我们给 someProperty 属性赋值并打印该属性的值,你认为它会 crash 么? 我们先说一下结论,如果这个属性的值为100,那么它不会 crash,如果是 100.1 那么就一定会 crash。 如果你马上就明白了其中的原理,那么恭喜你,你完全不用再花时间看此篇文章了。 如果你没有意识到其中的问题,不妨花上 10 分钟时间来仔细读读这篇文章的内容。
Tagged Pointer 是什么?
本节内容是节选自唐巧的文章 - 深入理解Tagged Pointer
在 2013 年的 WWDC 上,Apple 推出了首个 64 位架构的双核处理器,为了节省内存和提高执行效率,Tagged Pointer 概念诞生了。Apple 宣称引入该技术后,相关逻辑能减少一半的内存占用,以及 3 倍的访问速度提升,100 倍的创建、销毁速度提升。
为了能让大家更好的理解上面代码的问题,我们需要了解 Tagged Pointer 的一些实现细节。
我们知道 NSNumber、NSDate 一类的变量本身的值需要占用的内存大小常常不需要 8 个字节,拿整数来说,4 个字节所能表示的有符号整数就可以达到 20 多亿(注:2^31=2147483648,另外 1 位作为符号位),对于绝大多数情况都是可以处理的。
所以 Apple 将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。所以,引入了 Tagged Pointer 对象之后,64 位 CPU 下 NSNumber 的内存图变成了以下这样:
int main(int argc, char * argv[]) { @autoreleasepool { NSNumber *number1 = @1; NSNumber *number2 = @2; NSNumber *number3 = @3; NSNumber *numberFFFF = @(0xFFFF); NSLog(@"number1 pointer is %p", number1); NSLog(@"number2 pointer is %p", number2); NSLog(@"number3 pointer is %p", number3); NSLog(@"numberffff pointer is %p", numberFFFF); return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } 复制代码
运行之后,我们得到的结果如下,可以看到,除去最后的数字最末尾的 2 以及最开头的 0xb,其它数字刚好表示了相应 NSNumber 的值。
number1 pointer is 0xb000000000000012 number2 pointer is 0xb000000000000022 number3 pointer is 0xb000000000000032 numberFFFF pointer is 0xb0000000000ffff2 复制代码
可见,苹果确实是将值直接存储到了指针本身里面。我们还可以猜测,数字最末尾的 2 以及最开头的 0xb 是否就是苹果对于 Tagged Pointer 的特殊标记呢?我们尝试放一个 8 字节的长的整数到 NSNumber 实例中,对于这样的实例,由于 Tagged Pointer 无法将其按上面的压缩方式来保存,那么应该就会以普通对象的方式来保存,我们的实验代码如下:
NSNumber *bigNumber = @(0xEFFFFFFFFFFFFFFF); NSLog(@"bigNumber pointer is %p", bigNumber); 复制代码
运行之后,结果如下,验证了我们的猜测,bigNumber 的地址更像是一个普通的指针地址,和它本身的值看不出任何关系:
bigNumber pointer is 0x10921ecc0 复制代码
Tagged Pointer 带来的问题?
我们结合一下刚才的代码,试想如果我们有以下两种使用场景,哪一种会有问题呢?
- 代码片段 1
// CodeSnippets 1 self.view.someProperty = 100; NSLog(@"%@", @(self.view.someProperty)); 复制代码
- 代码片段 2
//CodeSnippets 2 self.view.someProperty = 100.1; NSLog(@"%@", @(self.view.someProperty)); 复制代码
不管你是通过代码实验的,还是已经想明白了,第二个代码片段是会造成 crash 的,而且 Xcode 会提示这是一个野指针方面的问题。
那么结合刚才所说的 Tagged Pointer,我们怎么解释这个问题呢?
当赋值为 100 时,由于 Tagged Pointer 的优化, @(100)
生成的 NSNumber 对象并不是一个严格意义上的对象,所以系统不会在堆上开辟内存存储对象,值是直接保存在地址指针里面,在取出的时候也就不会出现任何释放的问题。
而当赋值为 100.1 时,由于 Tagged Pointer 对这种值没法进行优化,@(100.1) 生成的 NSNumber 对象是一个真正意义上的对象,所以此时存储下来的是一个地址指针,但由于在关联的时候我们选用了 OBJC_ASSOCIATION_ASSIGN
,那么此时系统并不会帮我们去进行那些计数引用等操作,所以当我们想再取出的时候,就会出问题了,也就是野指针的问题。
至此,应该很好的解释了前言中那段代码的问题。
结论
那么我们应该如何优化这段代码呢?
- 假设你真的想存一个 CGFloat,由于在存取过程中操作的是一个对象,不妨将
objc_AssociationPolicy
选项中的OBJC_ASSOCIATION_ASSIGN
换成OBJC_ASSOCIATION_RETAIN_NONATOMIC
。 - 如果可以的话,建议直接保存一个 NSNumber 类型的对象来替换 CGFloat 类型的数值,毕竟这样会更安全,记得同样把关联策略设定为
OBJC_ASSOCIATION_RETAIN_NONATOMIC
。
关于 CGFloat 还需要注意的一点是:在不同的 CPU 下,它的真实类型是不一样的,有时是 float,有时是 double,那么在从 NSNumber 做转换的时候,到底要返回何种类型,仍然需要开发者做好判断。
- 在 Apple 的 API 文档中,
OBJC_ASSOCIATION_ASSIGN
说是用于 weak 类型的引用,但并不是 ARC 范围内的 weak,严格来说应该更类似于unsafe_unretained
的概念,如果真的要用OBJC_ASSOCIATION_ASSIGN
策略,请时候考虑好对象的生命周期,避免不必要的 crash 。
参考 NSHipster 的文章Associated Objects
- 如果你是声明一个 ARC 范围内的
weak
属性,你可能需要一个类似弱引用表概念的东西,不妨阅读下瓜神的这篇文章 weak 弱引用的实现方式
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 2018联想全球超算峰会召开,发布联想AI产品家族
- Android RxJava 实战系列:联想搜索优化
- 联想HPC:独步青云之后,重在行业普惠
- Trie树实现搜索引擎自动联想
- 联想回应“反对预装国产系统”消息:谣言,一直支持国产
- 联想和微软开展物联网合作 基于Azure平台
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
UNIX 时间戳转换
UNIX 时间戳转换