iOS 内存管理那些事

作者: Cstars | 来源:发表于2016-11-01 20:24 被阅读100次

          最近在拉钩看到一个面试评论,有个哥们在优酷面完后评论--“ARC下有谁会用autorelease,问我autorelease后的对象何时释放,老子能知道?要是在流媒体开发中会用到它我脑袋削下来给你当板凳。”

          看完评论的我也陷入沉思,这似乎优点像以前的我---总在找别人的毛病,曾经的我一直觉得iOS开发哪需要什么算法,整天在写UI,顶多写个冒泡排序,面试算法多此一举。不过经过多轮的面试,心境早已变了,开始尝试着站在对方角度看问题,毕竟抱怨对自己没有一点助益,现在的我都开始啃《算法导论》《代码大全》。对那个兄弟,我只想说----停止抱怨,你会得到更多。作为一个iOS程序员(快满大街的职业),说自己会自动布局,会写tableview代理数据源,...,怎么能凸显出你的专业素养与逼格?我们需要打造自己的技术护城河,最近研究OpenGL ES也有些心得,过段时间可能写图像处理方面的blog.


          闲话不说了,步入正题,本文主要围绕MRC下内存管理概念及重要的注意点(这对于理解ARC内存管理也很有帮助,而且面试经常遇到)

    本文中大部分内容来自Advanced Memory Management Programming Guide,有兴趣的可以自行参考文档,我也会在适当地方给出原文。

    一.MRC下的内存管理(先简单回顾,重点在后面)

    无论在MRC还是ARC下都是采用引用计数来管理内存,只不过在ARC下编译器在编译时为你自动插入了retain,release,autorelease等内存管理语句,当对象的引用计数为0时会被释放。

    retain,release语句成对出现,retain引用计数+1,release引用计数-1.

    基本的内存管理原则:

    1.使用alloc,new, copy, mutablecopy创建的对象,你自动获得所有权,即引用计数加1.

    2.要获得一个对象的所有权,使用retain.

    3.当你不需要使用时,释放你拥有的对象,使用release,autorelease.

    注意点:

    1.autorelease的出现是为了实现返回对象的方法。(可能是原因之一吧)

    - (NSString *)name {

              NSString *str = [[NSString alloc] initWithFormat:@"hello"];

    //在MRC下,若不用autorelease,无法返回str,像这样,str将会泄漏。你必须想办法释放它。若直接释放,则在返回之前str就已经无效了。

             return str;

    }

    2.集合类对象自动对存入的对象进行retain,移除对象或者销毁集合本身时也会解除拥有关系,发送release。

    3.不要使用dealloc方法管理稀缺资源。

    什么稀缺资源?原文这样说--You should typically not manage scarce resources such as file descriptors, network connections, and buffers or caches in a dealloc method.

    原因:dealloc可能会被推迟执行,甚至被绕过不执行。(什么时候会绕过呢,当app被结束时,系统不会挨个调用dealloc回收内存,这样比较低效,系统直接自己释放回收了app占用的内存。)

    导致的严重问题:1.资源得不到正确释放,比如文件资源,那磁盘将会被占满,用户无法存储东西(If your application runs out of file descriptors, for example, the user may not be able to save data.)比内存泄漏严重多了,是不是需要清理磁盘或重装系统。

    2.清理的逻辑在错误的线程执行。若对象在不恰当时间autorelease,它将在不确定的某个线程的自动释放池中销毁,对于有些资源要求只能在一个线程访问来说,后果是严重的。(If an object is autoreleased at an unexpected time, it will be deallocated on whatever thread’s autorelease pool block it happens to be in. This can easily be fatal for resources that should only be touched from one thread.)

    面试中的那些问题

    1.autorelease的对象什么时候释放?

    在它所在的最内层自动释放池结束时,它会收到release消息释放。每一个线程都维护着属于自己的自动释放池栈。

    2.什么样的对象会被放入自动释放池?

    收到autorelease消息的对象,在ARC下,若不是用alloc, new,copy, mutablecopy创建的对象,一般可以认为系统自动添加了autorelease,所以会被放入自动释放池,而alloc等创建的不会放入,在ARC下若要尽早释放alloc等方法创建的对象,只需要赋值nil就好了。没有强引用就会自动释放。自动释放池只对接收了autorelease消息的对象有用。

    3.多线程与自动释放池,新创建的线程需要创建释放池吗?

    若使用NSThread创建的线程,需要创建释放池,否则会造成内存泄漏。默认只在主线程的运行循环中自动创建自动释放池,所以一般有关UI的操作,不需要手动创建自动释放池,在每一个循环结束时,释放池中的对象会被释放。(Each thread in a Cocoa application maintains its own stack of autorelease pool blocks. If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block.)

    4.使用gcd 、OperationQueue等创建的线程需要创建释放池吗?

    不需要,我在Concurrency Programming Guide中找到一段话,Although GCD dispatch queues have their own autorelease pools, they make no guarantees as to when those pools are drained. If your application is memory constrained, creating your own autorelease pool allows you to free up the memory for autoreleased objects at more regular intervals.

    大致意思就是,gcd的队列有自己的自动释放池,但是不保证什么时候释放,如果你需要尽快释放内存,你可以在block中创建自己的自动释放池以便更频繁地释放。所以一般情况下不需要,除非你内存很吃紧,需要极力优化。因为OperationQueue等高层API是基于GCD的,所以可以大胆猜想,它必然也是有自己的自动释放池。

    5.什么时候需要使用自动释放池?

    官方给出的三中情形:

    1.If you are writing a program that is not based on a UI framework, such as a command-line tool.(若写的程序无关UI,比如命令行程序,)

    2.If you write a loop that creates many temporary objects.(在for循环中创建过多临时对象,可以用来尽早释放,避免内存峰值过高)

    You may use an autorelease pool block inside the loop to dispose of those objects before the next iteration. Using an autorelease pool block in the loop helps to reduce the maximum memory footprint of the application.

    3.If you spawn a secondary thread.(你创建了第二个线程)

    You must create your own autorelease pool block as soon as the thread begins executing; otherwise, your application will leak objects. (SeeAutorelease Pool Blocks and Threads blog for details.)你需要在线程开始执行时尽早创建自己的自动释放池,否则你的应用将会内存泄漏。

    最后:第一次写总结,难免有纰漏,若大家发现有问题,希望及时指正。

    抛出一个问题:Don’t Use Accessor Methods in Initializer Methods and dealloc,文档中这句有没理解,为什么在MRC下不允许在init和dealloc中使用属性访问方法而是直接使用实例变量?

    相关文章

      网友评论

        本文标题:iOS 内存管理那些事

        本文链接:https://www.haomeiwen.com/subject/wywmuttx.html