iOS 内存管理 基础回顾

653 阅读5分钟

深入理解代替单纯记忆

MRR----manual retain release

苹果官方把手动内存管理叫做叫做MRR,并不是大家常说MRC

从MRR到ARC

  • MRR和ARC本质上都基于引用计数机制管理对象内存

  • 所以从引用计数机制产生之初,内存管理的原则就没有变

    • 以alloc、copy、new开头的方法获取的对象,默认引用计数都是加过1的,所以方法调用方就拥有了对象的所有权;
    • 通过其他方法如getObject获取的对象则不会拥有对象所有权,一般getObject方法会通过执行autorelease方法延迟对象的释放,来使得调用getObject的方法中,对象暂时不被释放
    • 谁拥有所有权,谁负责释放;不拥有所有权,不能擅自释放对象
    • 通过retain来获取对象所有权,可以避免对象被释放
  • MRR下有三大核心方法管理内存: retainreleaseautorelease

  • ARC则将手动执行内存管理方法的工作交给了编译器

    • 为property带来了strong和weak特性
    • 相应的变量修饰符,__weak__strong__autoreleaseing
    • 当出现不同特性时,编译器会在适当位置插入retainrelease代码
  • ARC与MRR混用

    • 两者之间通过方法的命名规范来实现对象所有权的管理
    • 比如1个MRR的库中,如果出现了以"new、copy、alloc"开头的方法,ARC便知道谁执行该方法谁就拥有对象所有权
    • 反之亦然
    • 另外,ARC中为了使得在ARC和MRR混用时更安全,ARC规定方法名不能以"new、copy、alloc"开头
  • Managing Toll-Free Bridging

    • 因为CoreFoundation下的数据内存管理规则和Objective-C对象在ARC下的规则不同,CoreFoundation对象需要开发者手动管理
    • 在ARC下,OC对象和CF对象通过Toll-Free的类型转换互用时需要注意他们之间的差异
    • __bridge在bridging时不改变任何内存管理规则,比如从CF的a使用__bridge转为OC下的b时,那因为源对象是CF的,所以ARC不会释放b,而是需要通过CF的方式释放掉a,才能使得内存管理正常
    • __bridge_retainedCFBridgingRetain,用于将OC对象转为CF对象时,会将ownership交给CF对象,所以要按照CF的内存管理方式,在将来某个时机主动通过CFRelease方式释放对象
    • __bridge_transferCFBridgingRelease,用于将CF对象转为OC对象时,同样会将ownership交给OC的ARC中,所以由ARC来负责内存管理了,开发者无需加任何
  • ARC is supported in Xcode 4.2 for OS X v10.6 and v10.7 (64-bit applications) and for iOS 4 and iOS 5. Weak references are not supported in OS X v10.6 and iOS 4.

Transitioning to ARC Release Notes

自动释放池

提供一种基于引用计数内存管理机制的,延迟释放对象的机制

  • 在UIKit的主线程的每个runloop迭代中,系统会自动创建一个自动释放池

    • ApplicationKit在主线程的每个runloop的开始和结束时会创建和释放释放池
    • The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event.
  • 每个线程都会维护一个存放自动释放池的stack

    • 执行autorelease时,对象会被加到栈顶的池子中
    • 或者可以理解为,一个autoreleasing的block中执行了autorelease的对象,会加到最近的自动释放池中
  • 哪些情况下可以自己使用自动释放池

    • 当一些临时对象很占内存时,并且有可能堆积很多时
    • 当不是写Application Kit的程序,比如命令行的程序时,如果此时创建OC的对象并autorelease,如果运行时间比较长,可能导致内存累积无法释放对象
  • 陌生的__autoreleasing

    • 曾经一度不理解一个方法声明中用__autoreleasing修饰的NSError **error。其实本质上是OC内存管理机制要求必须存在的一个东西
    • 前面内存管理原则部分我们分析了,通过方法命名规范来决定执行者是否持有返回的对象。那么除了allocgetObject这种方法能够获取对象,还有像下面获取error这种获取对象的方式
    NSError *error;
    BOOL OK = [myObject performOperationWithError:&error];
    
    • 问题1来了,这种方式获取的对象该如何进行内存管理呢
    • 通常情况下,这个方法会声明__autoreleasing的error变量
    • 这是为了告诉调用方,我在这个方法内部创建了这个error,并且执行了autoRelease,这个error会延迟释放,调用方无需处理
    • 说白了这里的__autoreleasing修饰符是给调用方看的,本质上和alloc方法的命名规范功能一样
    • 问题2来了,NSError *error等价于NSError * __strong error,方法执行完获取到error后,后面会不会error无法释放?
    • 不会的,因为当我们这样写的话,编译器会自动为我们插入如下代码
      NSError * __strong error;
      NSError * __autoreleasing tmp = error;
      BOOL OK = [myObject performOperationWithError:&tmp];
      
  • 为什么main中要有一个自动释放池

    • main中加入自动释放池,为的是再程序最外层加入自动释放池,当程序意外或一些情况导致结束运行时,能释放哪些不能通过ruloop来释放的autorelease的对象
  • 通过retainCount方法返回的结果可能让我们很懵逼

    • 官方不建议用该方法来验证引用数
    • There should be no reason to explicitly ask an object what its retain count is (see retainCount). The result is often misleading, as you may be unaware of what framework objects have retained an object in which you are interested.

参考