Reference Counting Brief Analysis-浅谈ObjC引用计数

458 查看

原文链接Reference Counting Brief Analysis

Begin

这篇文不像动画那么有趣。记录的是我在学习Reference Counting中遇到的坑。倘若你之前只是听说过但是没有具体实践过,我想你在阅读本文后会有很大的收获。

Manual Reference Counting Introduce

笔者学习iOS开发一年有余,对于我的iOS入门书籍,是Objective-C Programming: The Big Nerd Ranch Guide,在23.4部分,对Referrence Counting会有介绍。我们对MRC最初的认识就是,retain计数加一,release计数减一,autorelease计数将来会减一,retainCount可以返回引用计数。当引用计数减到0时,系统将会自动调用对象的dealloc方法,你可以把它类比成C#中的dispose方法,而原先的开发人员可以在dealloc中释放或清理资源。

也许第一遍看这些知识你正处于一个入门开发学习状态而没有进行试验,当我们深入去学习这门语言之后,你会发现很多问题。接下来,我们一一提出并解决。

alloc/retain/release/dealloc是如何实现的?

由于Cocoa framework的闭源,我们只能通过其互换框架GNUstep来了解其原理。首先我们通过alloc方法入手。

alloc方法会调用NSAllocateObject函数。具体是做什么的呢?我们往后看。

1
NSAllocateObject函数通过调用NSZoneMalloc函数来分配存放对象所需的内存空间,之后将该内存空间置0,最后返回作为对象而使用的指针。那么NSZone又是什么?之前我想让大家移至CocoaDev,但是现在作者不在维护了。这里所说的NSZone我做了一下翻译:

从大体上来说,NSZone是Apple分配和释放内存的一种方式,它不是一个对象,而是使用C语言中的结构体来存储关于对象的内存管理信息。基本上,不需开发者去管理它,Cocoa Application使用一个默认的NSZone来对应用的对象进行管理。但是当默认的NSZone里面管理了大量数据的时候,你会想要一个自己控制的NSZone。中重视和,大量对象的释放可能会导致严重的内存碎片化,Cocoa本身有做过优化,每次alloc时会尝试着填满内存空隙,但如此开销会很大。于是,为了优化效率,你可以自己创建NSZone,当你有大量的alloc请求时,就全部转移到指定的NSZone,便可减少大量的时间开销。而且,使用NSZone还可以一次性的将你创建在NSZone的东西全部清除,避免逐个dealloc

熟悉C或者C++的读者,读过以后可以立马反应到,这其实就是一个官方封装的内存池。无论是优化内存碎片化还是对象统一释放,都是内存池的显著特点。总的来说,当你需要大量创建对象的时候,使用NSZone能提高效率的。在Cocoabuilder中,有一篇叫what’s an NSZone?的帖子中,Timothy J. Wood写道:由于历史原因,现在已经不能创建一个真正的NSZone,而是在Main Zone中创建一个Child Zone,这样不会使存储单元过度碎片化。发表日期是2002年,也就是说,Cocoa很早之前就已经注意到内存碎片的危险,而改善了Zone方法。

再来说一下retainrelease。我们也从GNUstep源码入手:

大概扫一遍代码,其实我们只是对计数的上线做了一个判断。UINT_MAX - 1这是个什么东西呢。直接敲到Xcode中发现这个值得18446744073709551615,转换成二进制1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111,也就是我们常说的2^64 – 1。这个值也就是-1在内存当中的补码存储形式。记住这个值,我们后面还会遇到。