编写高质量代码的52个有效方法(五)—内存管理

编写高质量代码的52个有效方法(五)—内存管理_第1张图片
outstanding.png

29.理解引用计数

引用计数工作原理:在引用计数架构下,对象有个计数器,用以表示当前有多少个事物想令此对象继续存活下去。这在Objective-C中叫做“保留计数”(retain count),不过也可以叫“引用计数”(reference count)。NSObject协议声明了下面三个方法用于操作计数器,以递增或递减其值:

  1. retain 递增保留计数
  2. release 递减保留计数
  3. autorelease 待稍后清理“自动释放池”(autorelease pool)时,再递减保留计数。

一个对象从创建到释放的整个过程:对象创建出来时,其保留计数至少为1.若想令其继续存活,则调用retain方法。要是某部分代码不再使用此对象,不想令其继续存活,那就调用release或autorelease方法。最终当保留计数归零时,对象就回收了(deallocated),也就是说,系统会将其占用的内存标记为“可重用”(reuse)。此时,所有指向该对象的引用也都变得无效了。

要点:

  1. 引用计数机制通过可以递增递减的计数器来管理内存。对象创建好之后,其保留计数至少为1.若保留计数为正,则对象继续存活。当保留计数将为0时2,对象就被销毁了。
  2. 在对象生命周期中,其余对象通过应用来保留或释放此对象。保留与释放操作分别会递增及递减保留计数。

30.以ARC简化引用计数

由于ARC会自动执行retain、release、autorelease等操作,所有直接在ARC下调用这些内存管理方法是非法的。具体来说,不能调用下列方法:

  1. retain
  2. release
  3. autorelease
  4. dealloc

要点:

  1. 有ARC之后,程序员就无需担心内存管理问题了。使用ARC来编程,可省去类中的许多“样板代码”。
  2. ARC管理对象生命期的办法基本上就是:在合适的地方插入“保留”及“释放”操作。在ARC环境下,变量的内存管理语义可以通过修饰符指明,而原来则需要手工执行“保留”和“释放”操作。
  3. 由方法所返回的对象,其内存管理语义总是通过方法名来体现。ARC将此确定为开发者必须遵守的规则。
  4. ARC只负责管理Objective-C对象的内存。尤其要注意:CoreFoundation对象不归ARC管理,开发者必须适时调用CFRetain/CFRelease.

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

//ARC模式下的内存管理
- (void)dealloc{
    CFRelease(coreFoundationObject);
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

要点:

  1. 在dealloc方法里,应该做的事情就是释放指向其他对象的引用,并取消原来订阅的“键值观测”(KVO)或NSNotificationCenter等通知,不要做其他事情。
  2. 如果对象持有文件描述符等系统资源,那么应该专门编写一个方法来释放此种资源。这样的类要和其他使用约定:用完资源后必须调用close方法。
  3. 执行异步任务的方法不应再dealloc里调用;只能在正常状态下执行的那些方法也不应在dealloc里调用,因为此时对象已处于正在回收的状态了。

32.编写“异常安全代码”时留意内存管理问题

    Object *obj = [Object new];
    
    @try {
        [obj doSomethingMayThrow];
    } @catch (NSException *exception) {
        NSLog(@"wawo,抛异常了");
    } @finally { 
        [obj close];
    }

@finally:无论是否抛出异常,其中的代码都保证会运行,且只运行一次。

要点:

  1. 捕获异常时,一定要注意将try块内所创立的对象清理干净
  2. 在默认情况下,ARC不生成安全处理一次所需的清理代码。开启编译器标志后(-fobjc-arc-exceptions),可生成这种代码,不过会导致应用程序变大,而且会降低运行效率。

33.以弱引用避免保留环

保留环也就是我们常说的循环引用。保留环通常会造成内存泄漏。

垃圾回收:Mac OS X平台的Objective-C程序有个选项,可以启用垃圾收集器(garbage collector),它会检测保留环,若发现外界不再引用其中的对象,则将之回收。但是,从Mac OS X 10.8开始,垃圾收集机制就废弃了,而且iOS系统从未支持过这项功能。因此,从一开始编码时要注意别出现保留环。避免保留环的最佳方式就是弱引用。这种引用经常用来表示“非拥有关系”(nonowning relationship)。将属性声明为unsafe_unretained或weak。

用unsafe_unretained修饰的属性特质,其语义同assign特质等价。然而,assign通常只用于“基本类型”(int float 结构体等), unsafe_unretained则多用于对象类型。这个词本身就表明其所修饰的属性可能无法安全使用。

Objective-C中还有一项与ARC相伴的运行期特性,可以令开发者安全使用弱引用:这就是weak属性特质,它与unsafe_unretained的作用完全相同。然而,只要系统把属性回收,属性值就会自动设为nil。

区别:unsafe_unretained与weak属性,在其所指的对象回收以后表现出来的行为不同。当指向实例的引用移除后,unsafe_unretained属性仍然指向那个已经回收的实例,而weak属性则指向nil。

要点:

  1. 将某些引用设为weak,可避免出现保留环。
  2. weak引用可以自动清空,也可以不自动清空。自动清空(autonilling)是随着ARC而引入的新特性,由运行期系统来实现。在具备自动清空功能的弱引用上,可以随意读取其数据,因为这种引用不会指向已经回收的对象。

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

Objective-C对象的生命周期取决于其引用计数。在Objective-C的引用计数架构中,有一项特性叫做“自动释放池”(autorelease pool)。释放对象有两种方式:一种是调用release方法,使其保留计数立即递减;另一种是调用autorelease方法;将其加入“自动释放池”中。自动释放池用于存放那些需要在稍后某个时刻释放的对象。清空(drain)自动释放池时,系统会像其中的对象发送release消息。

int main(int argc, char * argv[]) {

    @autoreleasepool {
    
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        
    }
    
}

写在main函数里的这个自动释放池可以理解为最外围捕捉全部自动释放对象所用的池。

要点:

  1. 自动释放池排布在栈中,对象收到autorelease消息后,系统将其放入最顶端的池里。
  2. 合理运用自动释放池,可降低应用程序的内存峰值
  3. @autoreleasepool这种新式写法能创建出更为轻便的自动释放池。

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

向已经回收的对象发送消息是不安全的,也就是一个空指针调用方法会终止应用程序,同时打印 message sent to deallocated instance ox58s5a5fg5o 类似的信息。开启 “僵尸对象”(Zombie Object)功能,运行期系统会把所有已经回收的实例转化成特殊的“僵尸对象”,而不会真正回收它们。僵尸对象收到消息后,会抛出异常,其中准确说明了发送过来的消息,并描述了回收之前的那个对象。

开发方式:编辑应用程序的scheme,在对话框左侧选择“Run”,然后切换至“Diagnostics”分页,最后勾选“Enable Zombie Objects”选项。

要点:

  1. 系统在回收对象时,可以不将其真的回收,而是把它转化为僵尸对象。通过环境变量NSZombieEnabled可开启此功能。
  2. 系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使对象变成僵尸对象。僵尸类能够响应所有的选择子(方法),响应方式为:打印一条包含消息内容及其接受者的消息,然后终止应用程序。

36.不要使用retainCount

//MRC
- (NSInteger)retainCount;

Objective-C通过引用计数来管理内存。每个对象都有一个计数器,其值表明还有多少个其他对象想令此对象继续存活。对象创建好之后,其保留计数大于0.保留与释放操作分别会使改计数递增及递减。当计数变为0时,对象就为系统所回收并摧毁了。然而ARC已经将此方法废弃了。实际上,在ARC中调用,编译器就会报错,这和在ARC中调用retain、release、autorelease方法时的情况一样。

要点:

  1. 对象的保留计数看似有用,实则不然,因为任何给定时间上的“绝对保留计数”(absolute retain count)都无法反映对象生命期的全貌。
  2. 引入ARC之后,retainCount方法就正式废止了,在ARC下调用该方法会导致编译器报错。



PDF格式的资料来自iOS开发交流群、感觉作者的贡献,对于知识的系统归纳总结很有帮助。
编写高质量代码的52个有效方法
编写高质量代码的52个有效方法(一)—熟悉OC
编写高质量代码的52个有效方法(二)—对象、消息、运行期
编写高质量代码的52个有效方法(三)—接口与API设计
编写高质量代码的52个有效方法(四)—协议与分类
编写高质量代码的52个有效方法(五)—内存管理
编写高质量代码的52个有效方法(六)—块与大中枢派发
编写高质量代码的52个有效方法(七)---系统框架

你可能感兴趣的:(编写高质量代码的52个有效方法(五)—内存管理)