iOS内存管理

iOS内存管理

引用计数(Reference Count)

iOS的内存管理基于引用计数这一机制,简单地说就是一个对象被其他对象持有时,引用计数+1,当不再被持有时-1,当引用计数减为零的时候对象不被任何人持有,就会被释放。

iOS内存管理

内存管理的思考方式

  • 自己生成的对象自己持有
    id obj = [[NSObject alloc] init];
  • 不是自己生成的对象自己也能持有
    id obj = [NSMutableArray array];
    [obj retain];
  • 不再需要自己所持有的对象时释放
    id obj = [[NSObject alloc] init];
    [obj release]
  • 不是自己持有的对象不能释放
    id obj = [NSMutableArray array];
    [obj release]
    程序崩溃(obj并不持有NSMutableArray对象)

对象操作与对应的Objective-C方法

对象操作 Objective-C方法 对应的操作结果
生成并持有对象 Alloc,new,copy,mutableCopy 等方法 生成对象并设置引用计数=1
持有对象 retain 引用计数+1
释放对象 release 引用计数-1
释放对象 dealloc(系统自动调用) 引用计数=0时调用

使用某个方法生成对象,其源码实现:

    -(id)allocObject
    {
        id obj = [[NSObject alloc] init];    //生成并持有了NSObject对象
        return obj;
    }

这里原封不动地将obj返回给方法的调用者,使得方法的调用者也持有该对象。
我们考虑另一种情况:

    -(void)object
    {
        id obj = [[NSObject alloc] init];   //生成并持有了NSObject对象
        [obj autorelease];                  //对象存在但不再持有
        return obj
    }

这里使用autorelease方法将obj对象注册到autoreleasepool中,在对象超出指定的生存范围的时候能够自动正确地释放(调用release方法)

autorelease

autorelease类似于C语言的自动变量的特性,当自动变量超出其作用域时,就会被自动释放。但是autorelease与C语言不通的是,你可以自己定义自动变量的作用域。
  autorelease的使用方法:
    ① 生成并持有NSAutoreleasePool对象
    ② 调用已分配的对象的autorelease实例方法
    ③ 释放NSAutoreleasePool对象

iOS内存管理

当我们在调用autorelease方法时,其内部其实是调用了[NSAutoreleasePool addObject:self]方法,从而将对象放入自动释放池中,当自动释放池被释放时,自动释放池里的对象也被一并释放了。

ARC

__strong修饰符

__strong修饰符是id类型和对象类型的默认修饰符
id obj = [[NSObject alloc] init]id __strong obj [[NSObject alloc] init]等价
__strong表示对对象的强引用,当变量超出其作用区域的时候,强引用会失效,此时对象不再被持有,就会被释放。

    {
        id __strong obj = [[NSObject alloc] init];
        //此时obj对NSObject对象强引用
    }
    //超出作用区域,强引用失效,NSObject对象不再被持有,于是被释放

使用__strong修饰的变量可以互相赋值

    id __strong obj0 = [[NSObject alloc] init]; //对象A
    id __strong obj1 = [[NSObject alloc] init]; //对象B
    id __strong obj2 = nil
    obj0 = obj1 
    /* obj0被赋值,所以其对对象A的强引用失效,对象A不再被
     * 持有,于是被释放
     * obj1对对象B有一个强引用,现在obj1赋值给obj0,于是
     * obj0也有对象B的强引用,此时对象B的强引用对象为
     * obj1、obj0
     */
    obj2 = obj0
    //同样的此时的obj0对对象B有强引用,当obj0赋值给obj2
    //之后obj2也有对对象B的强引用,此时对象B的强引用变量
    //为obj0、obj1、obj2
    obj1 = nil
    //因为obj1被置为nil,所以obj1对对象B的强引用失效
    //此时对象B的强引用对象为obj0、obj2
    obj0 = nil
    //因为obj0被置为nil,所以obj0对对象B的强引用失效
    //此时对象B的强引用对象为obj2
    obj2 = nil
    //因为obj0被置为nil,所以obj0对对象B的强引用失效
    //此时对象B不再被任何人持有,于是被释放

循环引用

下面我们用代码来重现循环引用,声明一个Test类

    @interface Test:NSObject
    {
        id __strong obj_;
    }
    -(void)setObject:(id __strong)obj;
    @end
    @implemnetaion Test
    -(id)init
    {
        self = [super init];
        return self
    }
    -(void)setObject:(id __strong)obj
    {
        obj_=obj;
    }
    @end

Test类有一个成员变量obj_,并且实现了obj_setter方法setObject:
下面为循环引用发生的代码

    {
        id test0 = [[Test alloc] init]; //Test对象A
        //test0对对象A有强引用
        id test1 = [[Test alloc] init]; //Test对象B
        //test1对对象B有强引用
        [test0 setObject:test1];
        //对象A的成员变量obj_对对象B有强引用
        //此时对象B的强引用变量有test1、对象A的成员变量obj_
        [test1 setObject:test0]
        //对象B的成员变量obj_对对象A有强引用
        //此时对象A的强引用变量有test0、对象B的成员变量obj_
    }
    /* 出了作用域
     * test0对对象A的强引用失效,释放对象A,但是此时对象B的成员
     * 变量obj_还持有对对象A的强引用,导致对象A无法释放
     * test1对对象B的强引用失效,释放对象B,但是此时对象A的成员
     * 变量obj_还持有对对象B的强引用,导致对象B无法释放
     * 于是内存泄漏
     */
iOS内存管理

当只有一个对象时,也可以发生循环引用,例如

 id test = [[Test alloc] init];
 [test setObject:test];

此时

iOS内存管理

__weak修饰符
weak

更新中