微信号:JoneTalk

介绍:热爱生活,分享感悟;研究技术,共同成长.

内存管理之引用计数

2015-06-15 18:43 小飞

内存,乃代码容身之所。欲码好代码,必先深入代码之源。
程序运行期的内存由堆和栈构成。理解Objective-C内存管理之前理解堆与栈的知识很有必要。本文采用通俗的方式阐述个人对Objective-C内存管理中引用计数部分的理解。

堆与栈


栈内存(stack):栈内存是线程被创建完成后系统预先分配的一段内存(容量已确定)。该段内存的作用是当函数被调用时,将函数加载到栈中。具体的实现又是怎么样呢?众所周知书写一个函数时都要用一对大括号({ })包起来,其实大括号就是将函数定义为了一个块(block)。当函数被执行时,该函数会以块的形式被压入栈的顶部,其中包含这个函数中的局部变量以及bookKeeping数据,当函数执行完毕返回时,这块栈内存就会被系统回收(出栈)用于下一次调用函数的覆写。由此可以看出栈是一种先进后出的数据结构(LIFO)。栈在这整个过程中的具体实现仅是简单的移动始终指向栈顶的指针罢了。

堆内存(heap):堆内存是动态分配的。跟栈内存不同的是其没有强制的模式要求来分配或者释放内存,释放和分配内存由使用者自己掌控。堆内存可以被不同线程所共享,但是每个线程的栈空间却是相对独立的。

两者联系:一般对象创建后被分配在堆内存,指针变量被分配在栈内存中。使用栈内存中的指针变量指向堆内存中创建好的对象,便可以获取到这个对象。

引用计数(MRC)


引用计数(Reference Count )是Objective-C中内存管理的中枢。以下内容讲述的都是自动引用计数(ARC)之前的手动引用计数(MRC)。
当对象被创建时会被分配在堆内存中。要获取刚刚创建的这个「这个对象」,只能通过访问它的堆内存地址。如何访问堆内存?就是使用指针,只需要简单的指明指针的类型,取一个响亮的名字前面再加一颗「*」(除id类型外),指针变量就被轻松的在栈内存中创建了。示例如下:

id MacTalk

最后只需简单的将「这个对象」等号过去,便将「这个对象」的内存地址赋值给了指针变量(该地址将作为指针变量的内容)。熟悉的形式如下:

id MacTalk = [[NSObject alloc] init];

此时MacTalk持有堆内存中的「这个对象」,引用计数记为1。

一般情况下,对象被创建之后第一次赋值给的指针变量,被视为这个对象就是由该指针变量(MacTalk)所创建并持有的,后续会将这种指针变量称为「自己」。

然而,如果这时有个叫iOSTalk的家伙出来说我也想获取到MacTalk所持有的对象怎么办呢?看代码:

id iOSTalk = MacTalk; // 这么写被MacTalk读者发现会不会被打?
[iOSTalk retain];

此时iOSTalk也持有了MacTalk所创建的「这个对象」,引用计数加1,变为了2。 引用计数即代表当前堆内存中的「这个对象」被多少个栈内存中的指针变量所持有。

此处有个retain方法,作用是持有非自己生成的对象。如若不执行retain方法,「这个对象」的引用计数仍旧为1,当MacTalk释放「这个对象」时,引用计数变为0,后果是「这个对象」会被系统回收。iOSTalk也就再也无法访问「这个对象」了。

然,正当iOSTalk愉悦的享用着执行retain方法之后持有的「这个对象」时,被MacTalk读者发现并举报了,iOSTalk被迫必须得释放「这个对象」。就有了如下操作:

[iOSTalk release];

此时由MacTalk创建的「这个对象」的引用计数减1,变回原来的1,仅MacTalk持有;此时iOSTalk只是一个空指针,它所占用的栈内存在出了作用域范围(block)之后会被操作系统回收。

随着MacTalk所在的函数执行完毕,MacTalk指针变量占用的栈内存也会被系统回收。那他指向(当初在堆内存中创建并持有)的「这个对象」怎么办?恩,「这个对象」正迷茫的流离在堆内存中无法释放,这种情况被称作「内存泄露」(memory leak)。所以在出作用域之前MackTalk需要执行:

[MacTalk release];

release方法使得「这个对象」的引用计数减1,变为0。系统就会自动调用dealloc方法废弃「这个对象」。

这里仅涉及到内存管理中最基础的部分,只需记住引用计数对象所有权的四项基本原则:

  • 自己创建的对象自己持有(MacTalk持有「这个对象」),

  • 非自己创建的对象也可以持有(iOSTalk持有MacTalk创建的「这个对象」)。

  • 不再使用自己持有的对象时需要释放(调用release方法)。

  • 自己不持有的对象无法释放。

以上为个人对内存管理之引用计数部分的理解,如有异议欢迎拍砖。


最后来两则旧闻:

  1. WWDC2015上推出的iOS9最大亮点是支持分屏多任务(MultiTasking),这将会重振被大屏iPhone挤压后的iPad市场。由此可知软件对硬件设备的重要性。
    内存对程序的重要性也是一样一样的!

  2. 近两个月,Objective-C从英雄排行榜(TIOBE)前三甲跌落到第五名,未来还会持续保持自由落体。然而,Apple的新兴语言Swift关注度持续上升。并随着Swift 2.0的开源,支持iOS,OSX,Linux平台,将受到更多开发者的追捧。
    低头干活,记得抬头看路。

题图:stack overflow logo

参考(假)链接:
http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap

这是个人微信技术第二篇,如果你还没有读过我为什么写微信技术。点击阅读原文,可参看iOSTalk开篇。


 
iOSTalk 更多文章 庖丁解牛之Objective-C对象模型 Swift入门篇 Runtime基础 生命不止 AutoLayout实践技巧
猜您喜欢 CQRS及.NET中的参考资料 【1024前端技术峰会实录】Angular 2 核心模块剖析 【图文直播全文记录】酷狗音乐的大数据实践(纯干货) 南湖论坛┃谭俊:对互联网不正当竞争法律规制的建议 2015:微软开源年