美文网首页一个苹果OC底层相关
Effective Objective-C 2.0笔记(四)

Effective Objective-C 2.0笔记(四)

作者: _小沫 | 来源:发表于2019-03-18 23:11 被阅读0次

    第五章 内存管理

    第29条:理解引用计数

    • OC使用引用计数管理内存,引用计数机制通过递增递减的计数器来管理内存。对象创建后,保留计数为1。若保留计数为正,则对象继续存活,保留计数降为0时,对象被销毁
    • 对象有三个方法用于操作计数器:retain:递增保留计数,release:递减保留计数,autorelease:待自动释放池销毁时,再递减保留计数;
    • MRC下对象调用release,保留计数如果降为0,对象所占内存会被回收;再访问对象可能使程序崩溃;因为对象所占内存dealloc后,只是放回“可用内存池”中;若果对象内存未被覆写,那么该对象仍然有效,程序不会崩溃;反之才会造成崩溃;为了防止访问野指针造成的程序崩溃,在对象release后应手动将指针置nil
    [object release];
    object = nil;
    
    • 属性存取方法中的内存管理:若属性为“strong”类型,setter方法的处理方式为:保留新值再释放旧值,然后更新实例变量令其指向新值;
    - (void)setFoo:(Foo *)foo {
      [foo retain];
      [_foo release];
      _foo = foo;
    }
    

    这个顺序极其重要,假如还未保留新值就释放旧值,两个对象又指向了同一个对象,那么先执行的release操作可能导致系统将此对象永久回收,后续的retain操作则无法令这个被彻底回收的对象复生;

    • autorelease对象的释放时机:如果创建了自己的自动释放池,对象会在自动释放池销毁时释放(即出了@autoreleasepool { }作用域后释放);否则就是等到当前线程的下一次事件循环(Runloop)时释放,因为一次事件循环会开启新的自动释放池;由此,autorelease能延长对象生命周期;

    第30条:以ARC简化引用计数

    • ARC会自动执行retain, release,autorelease等内存管理操作,在ARC下调用remain,release,autorelease,dealloc,ratinCount等方法是非法的,编译会报错;
    • ARC在调用内存管理方法时,是直接调用底层C语言,这样性能更好,因为保留,释放操作比较频繁,直接调用底层函数能节省很多CPU周期;
    • ARC确定的硬性规定:方法名以alloc,new,copy,mutableCopy开头的方法创建对象,返回的对象归调用者所有(负责释放对象);若方法名不以这四个词语开头,则表示所返回的对象不归调用者所有,返回的对象会自动释放;实际上就是调用了autorelease方法;因为ARC的这个硬性规定,所以声明属性名称时不能以new等开头,因为这样属性setter方法的内存语意会分不清;
    • ARC也会执行手工操作无法完成的优化:在编译期,ARC会把能够相互抵消的retain,release,autorelease操作约简,如果发现同一个对象上执行了多次保留与释放操作,ARC有时可以成对移除这两个操作;
    • 应用程序中,可用下列修辞符来改变局部变量与实例变量的语义:
      __strong:默认语义,保留值;
      __unsafe_unretained:不保留值,变量不会自动清空,可能不安全,会发生野指针问题;
      __weak:不保留值,但变量会自动清空,是安全的;
      __autorelease:自动释放;
    • ARC只负责管理OC对象的内存,对于OC对象,ARC会自动生成回收对象所执行的代码,但是对于非OC对象,如CoreFoundation中的对象或由malloc()分配在堆中的内存,那么仍需手动清理。
    - (void)dealloc {
      CFRelease(_coreFoundationObject);
      free(_heapAllocaatedMemoryBlob);
    }
    

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

    在dealloc方法中,应该做的是:释放指向其他对象的引用;移除KVO或NSNotificationCenter通知;

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

    异常一般只应在严重错误后才抛出(第21条);
    在@try块中,如果先保留了某个对象,然后在释放它之前又抛出异常,除非@catch块能处理该问题,否则就发生内存泄露了;

    • MRC模式下解决方法:在@finally块中调用release释放对象,因为@finally块,无论是否抛出异常,代码都会运行;
        Object *ojbect;
        @try {
            object = [[Object alloc] init];
            exception
            ....
        }
        @catch(NSExpression *expression) {
            ...
        }
        @finally {
            [object release];
        }
    
    • ARC模式,默认情况下ARC不会自动处理这种情况,又不能调用release。对于异常处理更加棘手;解决方法是:通过设置-fobjec-arc-exceptions编译标志来开启ARC生成安全处理异常代码的模式;这可以使ARC安全处理异常;

    第33条:以弱引用避免保留环(循环引用)

    • 循环引用会导致内存泄露,避免循环引用的最佳方式就是弱引用;这种引用表示“非拥有关系”;
    • “非拥有关系”的修辞符有weak,assign,unsafe_unretained;assign一般用于基本类型(int ,float,struct),unsafe_unretained一般用于对象类型,是不安全的;weak也用于对象类型,但是安全的;assign也可以用于对象类型,但也是不安全的;weak不能用于基本类型;
    • weak之所以安全,是因为变量指向的实例被释放回收时,weak属性会指向nil,而unsafe_unretained属性仍然指向已被回收的对象,这时在访问属性时会发生错误;
    • weak属性自动置为nil的原理:weak属性对象会被写入一张哈希表中,以weak属性指向的对象地址为key,weak指针为value;当指向的对象销毁时,会根据对象地址去表中查找weak指针并置为nil;

    第34条:以自动释放池降低内存峰值

    非alloc,new,copy,mutableCopy词开头方法创建的对象,都是autorelease对象,如果没有手动创建自动释放池,那么autorelease对象要等到下个Runloop后才释放;如果在下个Runloop之前,autorelease对象已经很多(比如for循环创建对象),那么内存峰值将会很高;那么,可以在适当的时机,手动创建autorelease pool使得对象及时释放以降低内存:

          for (int i = 0; i < 100000; i ++) {
                @autoreleasepool {
                    Object *obj = [Object ojbect];
                }
            }
    

    相关文章

      网友评论

        本文标题:Effective Objective-C 2.0笔记(四)

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