前言

Effective Objective C 读书笔记 - 内存管理

笔记

理解引用计数

  1. 引用关系有一个引用树,在iOS中,这个根引用对象是UIApplication

  2. 调用alloc,引用计数加1,表示该对象想存活下去

  3. 即使对象的引用计数为0,其占用的内存已经被dealloc了,但是注意:这个时候它的内存只是放回了“可用内存池”,里面的数据不会立即被覆盖

  4. 记住一点:在MRC中,将指针指向对象是不会增加reference count的,必须手动加1

  5. 看看下面的MRC代码:

    1
    2
    3
    4
    5
    6
    7
    - (NSString *)stringValue {
    NSString *str = [[NSString alloc]] initWithFormat:@"vanney"; // 1
    return str;
    }

    NSString *str = [self stringValue];
    [str retain]; // 2

    上面的代码在注释1,和注释2处分别增加了一次reference count,但是讲道理这个时候只需要一个reference count就可以了,所以会出现一些些小问题。这就需要Auto Release Pool

    1
    2
    3
    4
    5
    6
    7
    - (NSString *)stringValue {
    NSString *str = [[NSString alloc]] initWithFormat:@"vanney";
    return [str autorelease];
    }

    NSString *str = [self stringValue];
    [str retain];
  6. 注意retain cycle,也就是相互引用

以ARC简化引用计数

  1. 代码中不再需要写:retainrelease方法,但是这两个方法实际上还是执行了的,他们由ARC帮你执行
  2. ARC中调用这些方法绕过了消息转发,直接调用底层的C函数,更加快速
  3. 如果autorelease后面跟上的是retain,那么ARC可以优化这两个操作(因为这两个操作加在一起,什么也没做…)。这种优化通过设置标志位来实现,具体的看书~
  4. CoreFoundation的内容不适合ARC

在dealloc方法中只释放引用并解除监听

  1. delloc方法在对象reference count降到0时,会被调用
  2. 因为ARC会帮你生成释放对象所拥有的引用,那么你需要做的主要有两点
    1. 释放CoreFoundation对象
    2. 解除监听行为

以弱引用避免保留环

以“自动释放池块”降低内存峰值

  1. 自动释放池还有变量可见范围的限制

    1
    2
    3
    4
    @autoreleasepool {
    id object = [self createObject];
    }
    [self useObject:object]; // 报错,因为object变量已经不可见了

用“僵尸对象”调试内存管理问题

  1. 向已经回收的对象发送消息是非常不安全的。如果该内存已经被覆写了,那么程序就会崩溃;如果暂时没有被覆写,那么侥幸可以运行。所以这时候程序会偶尔报错,难以调试

  2. 这个时候就可以使用Cocoa提供的 僵尸对象 的调试方法

  3. 启用这一项调试功能之后:所有的本要被回收的对象会被转化成特殊的僵尸对象,这些内存不会被覆写;当僵尸对象收到消息后,他会将原本的对象信息打印出来,并提示说,你给一个已经回收的对象发送了信息

    enable zombie

  4. 至于僵尸对象的工作原理,具体的看书~

不要使用retainCount

  1. ARC已经废弃retainCount方法