天天看点

20160814Object-C内存管理二

我在开发的过程中,出现过内存占用越来越大,如何解决此类问题发生,方式方法很多,可以尝试"以自动释放池降低内存峰值"的方式。

自动释放池:

释放对象有两种方式,一是调用release,另一种是调用autorelease。一般情况下无需担心自动释放池创建的问题,系统会自动创建,比如,主线程或者是GCD会默认都有自动释放池的创建,每次执行"事件循环"时,就会将其清空。

举个例子:

NSArray *databaseRecords = /*....*/;
    NSMutableArray *people = [NSMutableArray array];
    for (NSDictionary *record databaseRecords) {
        SWPeople *person = [[SWPeople alloc] initWithRecord:record];
        [people addObject:person];
    }
           

如果读取数据库的数据很大,比如100000条,或者1000000000条,其中person这种临时的对象会越来越多,要等待系统稍后,就是进入下一个事件循环才能将其回收,因此,应用的内存会持续上升,所以面对这种情况,当循环的长度无法知道的时候,我们自己可以创建一个自动释放池,而不依赖与主线程的自动释放池。可以调整为下面的代码

NSArray *databaseRecords = /*....*/;
    NSMutableArray *people = [NSMutableArray array];
    for (NSDictionary *record databaseRecords) {
        @autoreleasepool {
            SWPeople *person = [[SWPeople alloc] initWithRecord:record];
            [people addObject:person];
        }
    }
           

如此一来,应用程序在执行循环中的内存峰值就会降低。自动释放池就像"栈"一样,创建好的对象压入栈中,释放对象从栈中弹出。在对象执行自动释放操作,相当于将其放入栈顶的那个池中。

用"僵尸对象"(Zombie Object)调试内存管理问题:

什么是僵尸对象:过渡释放的对象,所占用的对象已经被回收的对象。

对于内存管理的问题,以前我使用过Xcode Instruments 查找问题,现在也可以尝试使用僵尸对象查找问题。Xcode--->Scheme--->Diagnostics--->Enable Zombie Object 勾选。

正常情况下向已回收的对象发送消息时灵时不灵,具体要看该对象所占内存有没有被覆写。cocoa提供了僵尸对象(Zombie Object)这个功能,简单的说:启用该调试功能后,运行时会将所有已回收的实例转化为特殊的“僵尸对象”,而不会真正回收它们。这种对象在核心内存无法重用,因此不可能遭到覆写。僵尸对象收到消息后会抛出异常,其中说明了发送来的消息,并描述了回收之前的对象。(摘某博客)

僵尸对象的工作原理是什么,她的实现代码深深植于OC的运行其程序库、Foundation、CoreFoundation中,系统在回收对象的时候,将对象转化为僵尸对象,就是不可能遭到腹写,而不彻底回收。

其中有个重要的类,是类不是对象,_NSZombie_Class,这个类是在运行期产生的,是从_NSZombie的模板中复制出来的,她是用来充当标记的。使对象成为僵尸对象。

系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使该对象成为僵尸对象,僵尸类能够响应所有的的选择子,响应方式是:打印一条包含消息内容及其接收者的消息,然后终止应用程序。

有兴趣可以参考《编写高质量iOS与...52个有效的方法》P142。

废弃retainCount:

对象保留计数看似有用,其实不然,因为任何给定时间点上的"绝对保留计数"都无法反应对象生命期的全貌。

引入ARC之后,retainCount方法就正式废止了。

本文完。

继续阅读