|
|
51CTO旗下网站
|
|
移动端

临时分配与对象缓存

《iOS和macOS性能优化:Cocoa、Cocoa Touch、Objective-C和Swift》第7章内存:陷阱和优化技巧,本章将围绕这个话题来进行讨论。除此之外,我们将展示一些陷阱,尤其是在Objective-C 代码中经常出现的问题。本节为大家介绍临时分配与对象缓存。

作者:李俊阳 等译来源:电子工业出版社|2018-07-17 17:39

临时分配与对象缓存

正如在第3 章的“对象创建和缓存”一节中所看到的,临时对象对于Objective-C 程序而言是一个很严峻的挑战。临时对象的累积所造成的性能代价是非常高昂的,并且还可能会导致堆空间大量增长,即使我们已经非常小心不去构建n2 这样复杂的算法也是如此。

堆空间的增长可以通过内嵌的自动释放池或者新的@autoreleasepool 编译器指令来控制。示例7.6 中的describeOn:方法负责封装循环的所有迭代,这些循环迭代输出了内嵌数组的内容。如此就可以将那些不再需要的临时对象清理掉,特别是递归发送的describeOn:消息。

即便我们设法通过定期清理来避免堆空间的过度增长,但是临时对象仍然存在性能问题,因为它们的创建和销毁造成的性能开销非常昂贵。这是因为NeXT/AppleObjective-C 以及Swift 要求对象需在堆空间上分配,不允许像其他临时变量一样在栈上分配。从性能的角度来看,这种限制非常难受,因为栈分配所造成的性能损耗基本为零:为某个给定的函数添加一个“分配”所有栈变量的栈指针即可。

当然,由于讨论的是C 的超集,因此这意味着所谓不被允许的事情,实际上并不是无法做,而是真的不应当这么做。如果非要一意孤行,那么示例7.7 展示了如何在栈上分配Objective-C 对象。

1.获取类及类实例的大小。

2.使用alloca()和bzero()函数来分配和清除对象在栈中所使用的内存。

3.对类进行配置。

4.只在内存块的范围内进行使用,不能逾越。

示例7.7 Objective-C 对象的栈分配操作

  1. #import <MPWFoundation/MPWInteger.h> 
  2. #include <stdlib.h> 
  3. #include <objc/runtime.h> 
  4. int main(int argc, char *argv[]) {  
  5. Class mpwint=[MPWInteger class];  
  6. size_t instance_size=class_getInstanceSize( mpwint );  
  7. id a=alloca( instance_size );  
  8. bzero(a, instance_size );  
  9. object_setClass(a, mpwint);  
  10. [a setIntValue:43];  
  11. NSLog(@"a=%@",a);  
  12. return 0;  

虽然我曾经在某家位于Cupertino 的消费电子产品公司的生产代码中看到了这种做法,但是不得不承认我的尴尬癌都犯了。在过去的25 年时间中,尽管我痴迷于使用各种各样的Objective-C 黑科技来提升性能,但我从不使用这种做法。随着诸如非脆弱实例(non-fragile instance)变量的进步,这种做法完全是搬起石头砸自己的脚,因此强烈不推荐这样做。

至少,需要创建一个子类,阻止任何对此对象执行的retain 操作并抛出异常,并确保没有retain 操作之后它不会隐藏起来,显然,需要确保不能在方法/函数中返回这个对象,也不能对其使用ARC。

我们在第3 章的“可变性与缓存”一节中介绍了对象缓存的概念,一举解决了短生命周期对象的堆空间增长和分配/销毁开销的问题。我们不用每次都分配一个新的临时对象,然后寄希望于系统会及时将其销毁,也不用在栈上分配一个对象,然后寄希望于它不会逃逸,如果逃逸的话就会导致系统崩溃。我们所做的,只是重用那些已分配的对象,代价就是多了几个函数调用。

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Microsoft SQL Server 2005技术内幕:T-SQL程序设

SQL Server 2005微软官方权威参考手册。 是Inside Microsoft SQL Server 2005系列书中的第一本,SQL Server类的顶尖之作。 全球公认SQL S...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊