美文网首页
MRC期间的内存管理方式

MRC期间的内存管理方式

作者: foreverSun_122 | 来源:发表于2018-04-11 11:16 被阅读0次

    MRC引用计数规则

    内存管理的范围:任何继承了NSObject的对象,对基本数据类型无效(系统会自动回收)
    相关名词:

    • 内存泄漏:程序未能释放已经不在使用的内存
    • 僵尸对象:对象被销毁,不能再使用
    • 野指针(iOS):指针指向的对象已经被销毁,指向僵尸对象的指针,准确的说iOS中的野指针应该叫悬垂指针,对于野指针,要及时将其赋值为nil,成为空指针
    • 空指针:没有指向任何对象的指针

    内存管理的思考方式:

    • 自己生成的对象,自己持有
    • 非自己生成的对象,也能持有
    • 不再需要自己持有的对象时,释放
    • 非自己持有的对象无法释放
    操作 方法 思考方式
    生成对象 alloc/new/copy/mutableCopy 自己生成,自己持有
    持有对象 retain 非自己生成,也能持有
    释放对象 release 不再需要时,释放
    废弃对象 dealloc

    相关方法

    • alloc/new/copy/mutableCopy:生成对象,引用计数初值为1
    • retain:引用计数+1,并返回+1后的当前实例对象
    • release:引用计数-1,并没有释放内存
    • dealloc:实例对象销毁前,自动调用;引用计数为0,自动调用

    release与dealloc的区别:release释放的是对该对象的所有权,dealloc释放的是该对象占用的内存

    自己生成,自己持有(alloc/new/copy/mutableCopy)

    //生成并持有对象
    id obj1 = [[NSObject alloc] init];
    id obj2 = [NSObject new]; 
    NSString *str = @"123";
    NSString *str2 = [str copy];    //生成并持有str的副本
    
    NSMutableArray *array1 = [NSMutableArray alloc]init];
    NSMutableArray *array2 = [array1 mutableCopy];     //生成并持有array1的副本
    

    copy方法利用基于NSCopying方法约定,由各类实现的copyWithZone:方法生成并持有对象的副本。
    mutableCopy方法利用基于NSMutableCopying:方法约定,由各类实现的mutableCopyWithZone:生成并持有对象的副本。
    区别:copy生成不可变更的对象,mutableCopy生成可变更的对象

    注:以alloc/new/copy/mutableCopy开头,使用驼峰命名法的方法,也是自己生成并持有对象

    非自己生成,也能持有
    通过retain方法,让不是自己生成的对象与使用alloc/new/copy/mutableCopy方法生成的对象一样都能被持有。

    //取得,但不持有对象
    NSArray *array = [NSArray array];
    //持有对象
    [array retain]
    

    不再需要的对象,释放
    alloc/new/copy/mutableCopy生成并持有的对象,或者retain方法持有的对象,一旦不需要,无比要使用release进行释放。

    {
        //取得,但不持有对象
        id obj = [NSMutableArray array];
        //持有对象
        [obj retain];
        //释放对象
        [obj release];
    }
    

    注:
    内存中的常量对象(类对象、常量字符串对象等)的内存分配空间与其他对象不同,没有引用计数机制,永远不能释放这些对象,获取的retainCount结果返回为NSUIntegerMax(最大的无符号整数)

    非自己持有的对象无法释放
    释放后,再次释放

    id obj = [NSObject alloc]init];
    [obj release];
    [obj release];
    

    取得对象存在,但不持有时释放

    id obj = [NSObject object];
    [obj release];
    

    这些都会导致程序的崩溃

    MRC下autorelease的使用

    autorelease的具体使用方法:

    1. 生成并持有NSAutoreleasePool对象
    2. 调用已分配对象的autorelease实例方法
    3. 废弃NSAutoreleasePool对象
    while(...) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        /*
            在此进行一系列操作,调用临时对象的autorelease方法
        */
        [pool drain]; // 废弃pool
    }
    

    注:

    • 需要长时间运行的代码段或大量使用临时对象的代码可以通过autoreleasepoool提高内存利用率
    • 可以给实例多次发送retain,相应的也可以给实例多次发送autorelease,只要autorelease和retain要成对发送

    release与autorelease的区别
    release:调用后,立即释放对象
    autorelease:对象在超出指定的生存范围是能够自动并正确的释放(调用release方法)
    过程[obj autorelease]不立即释放变量obj所指的对象,而是注册到autoreleasepool中,pool结束时自动调用release

    1. 以alloc为头,使用驼峰命名法的方法,如何实现自己生成,自己持有的?
    - (id)allocObject 
    {
        //obj生成并持有对象
        id obj = [NSObject alloc]init];
        return obj;
    } 
    
    id obj = [NSObject allocObject];
    
    1. 如何实现[NSArray array]这种取得对象存在,但并不持有的?
    - (id)object
    {
        //obj生成并持有对象
        id obj = [[NSObject alloc]init];
        //obj注册到autoreleasepool中,这里的autoreleasepool一般都是main函数最开始生成的自动释放池
        [obj autorelease];
        return obj;
    }
    
    //取得对象,并不持有
    id obj = [NSObject object];
    [obj retain];   //持有对象
    

    MRC中要重写的方法

    1. dealloc:彻底释放一个对象,还需要释放该对象所持有的所有的对象的所有权
    - (void)dealloc
    {
        //所有持有的对象调用release方法,释放所有权
        [super dealloc];    //一定要调用父类的dealloc,内存的释放才会从子类一直上升到NSObject,对象才会彻底被释放
    } 
    
    1. setter方法

    基本类型:

    - (void)setObj:(NSUInteger)obj
    {
        _obj = obj;
    }
    

    对象类型:

    //以下写法参数obj与_obj是同一对象时,会出问题
    - (void)setObj:(id)obj {
        //obj与_obj都只指向同一对象,但obj不持有对象,这时release后对象
        //引用计数为0,对象被销毁,然后调用retain会出问题
        [_obj release];
        _obj = [obj retain];
    }
    
    //两种更安全的写法
    - (void)setObj:(id)obj
    {   
        //新对象所有权+1
        [obj retain];
        //旧对象所有权释放
        [_obj release];
        //将新对象赋给_obj
        _obj = obj;
    }
    
    - (void)setObj:(id)obj
    {
        if(_obj != obj)
        {
            //释放旧对象所有权
            [_obj release];
            //赋给_obj所有权+1后的新对象
            _obj = [obj retain];
        }
    }
    

    重写过后的setter方法与实例变量直接赋值的比较:

    id obj = [[NSObject alloc]init];
    self.obj = obj;     //重写过setter方法后,所有权会+1
    _obj = obj;         //实例变量直接赋值,所有权不变
    

    3、getter方法
    getter方法不属于alloc/new/copy/mutableCopy的自己生成自己持有,与[NSArray array]一样,都是取得非自己生成对象但不持有

    - (id)obj
    {
        id tmp = [obj retain];
        return [tmp autorelease];   //注册到autoreleasepool中,实现取得非自己生成对象但不持有,这里的autoreleasepool通常都是main函数里最开始生成的那个
    }
    
    int main(void) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        Object *obj = [Object obj];
        [pool drain];
    }
    

    总结:

    • 自己生成对象自己持有,只适用于以alloc/new/copy/mutableCopy开头的方法。
    • MRC的引用计数规则下,持有方法与释放方法一定要成对使用,确保对象被正确释放
    • 其他方式获得的对象,都属于取得非自己生成的对象,所以需要调用autorelease方法放入到autoreleasepool中。

    参考资料:

    • Objective-C编程全解
    • Objective-C高级编程iOS与OS X多线程和内存管理

    相关文章

      网友评论

          本文标题:MRC期间的内存管理方式

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