iOS 内存管理(一)

引用计数式内存管理的思考方式:

  • 自己生成的对象,自己所持有。
  • 非自己生成的对象,自己也可以持有。
  • 不再需要自己持有的对象时释放。
  • 非自己持有的对象无法释放。

对象操作

Objective-c方法

生成并持有对象

alloc/new/copy/mutableCopy方法

持有对象

retain方法

释放对象

release方法

废弃对象

dealloc方法

Cocoa 框架中 Foundation 框架中的NSObject 类担负内存管理的职责。内存管理中的alloc/retain/release/dealloc方法分别指代NSObject类的alloc类方法,retain实例方法,

release实例方法和dealloc实例方法。

 

1、        自己生成的对象,自己所持有   

使用下面名称开头的方法名意味着自己生成的对象只有自己持有。   

  • alloc 
  • new
  • copy
  • mutaleCopy
首先看前两个: alloc   new 

 //使用alloc类方法就能自己生成并持有对象,指向生成并持有对象的指针被赋给变量obj
        id obj = [[NSObject alloc] init];
        
        //使用new类方法也可以生成并持有对象,所以 alloc 和 new 二者是完全一致的
        id obj = [NSObject new];

纠正:上面代码注释中说到alloc 和new 二者是完全一致的,这种说法是不正确的!其实应该是:[NSObject new]  和  [[NSObject alloc] init] 这两个语句的作用是一致的。
 

下面看一下后两个 copy  mutableCopy

这两个的理解有点复杂。
copy 方法利用 基于NSCopying 方法约定,由各类实现的 copyWithZone: 方法生成并持有对象的副本。

与copy方法类似,mutableCopy 方法利用基于 NSMutableCopying 方法约定,有各类实现的 mutableCopyWithZone: 方法生成并持有对象的副本。

两者的区别在于:

copy 方法生成不可变更的对象,而mutableCopy方法生成可变更的对象。(这样说可能有点抽象,下面用一个例子来说明!)

        1. mutableCopy创建一个新的可变对象,并初始化为原对象的值,新对象的引用计数为 1

        2. copy 返回一个不可变对象。分两种情况:

        1)若原对象是不可变对象,那么返回原对象,并将其引用计数加 1

        2)若原对象是可变对象,那么创建一个新的不可变对象,并初始化为原对象的值,新对象的引用计数为 1


//创建一个不可变数组
        NSArray *immutableArray = [[NSArray alloc] initWithObjects:@"one",@"two",@"three", nil];
        
        //从不可变数组复制而来一个可变数组
        NSMutableArray *mutableArray = [immutableArray mutableCopy];
        [mutableArray addObject:@"four"];
        NSLog(@"count = %ld",(unsigned long)[mutableArray count]); //输出: count = 4

        NSMutableArray *array = [[NSMutableArray alloc] init];
        array = [mutableArray copy];
        //注意这里使用copy复制得到的是一个不可变的对象,尽管使用的是NSMutableArray类型。所以下面一条语句想向数组中添加元素,虽然编译成功,但是运行会出错。
        [array addObject:@"five"]; //运行出错

进一步说,copy就是浅拷贝,mutableCopy就是深拷贝

拷贝一个对象也就是创建一个新的实例,并且初始化为拷贝源的值。
对于像boolean,integer这类值,拷贝就是直接赋值。
对于指针形的object就分为浅拷贝和深拷贝。浅拷贝是只创建一个新的指针,并指向同一块数据。深拷贝就是数据和指针都创建

题外话:本来是想通过  retainCount 这个实例方法来观察 引用计数值的,但是发现有一个问题:

NSObject* obj = [[NSObject alloc] init];
    NSLog(@"%li",(unsigned long)[obj retainCount]); // print: 1
    [obj release];
    NSLog(@"%li",(unsigned long)[obj retainCount]); // print: 1

这段代码两次都是输出 1 ;第一个输出 1 ,应该没有什么问题,因为alloc 使得引用计数 +1  ;然后 release ,那么引用计数值 -1 ,那么就是应该输出 0呀!这个是为什么呢?

其实问题是这样的:

首先我们先看看文档中是如何介绍 retainCount 这个方法的:

Do not use this method. (required)


This method is of no value in debugging memory management issues. Because any number of framework objects may have

 retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of

 deferred releases on an object, it is very unlikely that you can get useful information from this method.


大概的意思是:这个方法在debug的过程中是没有什么价值的(大家就不要使用啦),因为系统会延迟释放对象,所以不太可能通过这个方法得到有用的信息。

所以呢?上面最后一个语句通过 retainCount输出 引用计数值 得到的信息是不准确的,其实已经释放了对象,只不过系统延时释放而已;也就说在最后一个输出语句执行的时

候,obj 这个对象其实还没有释放(引用计数值  还不为 0),你想,要是等于 0,那系统不是直接废弃对象了吗?那么你还怎么能够调用retainCount这个实例方法。

可以这样测试一下,如果再加一个release语句的话,程序就会崩溃,要不  have  a  try ! 这个是我的理解。


2、非自己生成的对象,自己也可以持有

用 alloc  new  copy  mutableCopy 以外的方法取得的对象,因为对象不是自己生成,所有对象不归自己持有,但是可以取得对象的存在。

//取得对象的存在,但是自己并不持有对象
    id obj = [NSMutableArray array];
    
    //通过retain可以持有对象
    [obj retain];

那么通过retain方法,非自己生成的对象也可以自己持有,就像跟用 alloc  new  copy  mutableCopy 方法生成对象并持有一样。

3、不再需要自己持有的对象时释放

自己持有的对象,一旦不需要,持有者有义务释放该对象,使用release方法。

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
    [label release];
    //已经释放的对象,不可以再访问
    label.text = @"hello world";
    //运行时会崩溃

4、

如果想要用某个方法生成对象,并将其返还给该方法的调用方,让调用方持有对象,如何实现呢?

-(id)allocObject {
    id obj = [[NSObject alloc]init];
    return obj;
}

注意方法的命名规则,要以alloc  new  copy  mutableCopy开头。

那么如果想通过某个方法取得对象的存在,而自己不持有对象,那么该如何实现呢?

-(id)object {
    id obj = [[NSObject alloc]init]; //自己持有对象
    
    [obj autorelease]; //取得对象的存在,但是自己不持有对象
    
    return obj;
}

注意方法的命名规则,不要以alloc  new  copy  mutableCopy开头,因为不持有对象。

上面使用了autorelease方法,用该方法可以取得对象的存在,但自己不持有对象。autorelease提供这样的功能,使对象在超出指定的生存范围时能够自动并正确的释放(调用 release方法)。

[obj autorelease]; 这个语句将obj对象注册到 autoreleasepool (自动释放缓冲池)中,在pool结束时自动调用release方法 。可以这样理解,让autoreleasepool 持有对象,让其负责对象的释放。


方法调用

id obj1 = [self allocObject]; //自己持有对象
    
    id obj2 = [self object]; //自己不持有对象,只是取得对象存在
    //当然也可以通过retain方法持有该对象
    [obj2 retain];


5、非自己持有的对象无法释放

对于用 alloc  new  copy  mutableCopy 方法生成并持有的对象,或者是用 retain方法持有的对象,由于持有者都是自己,所以在不需要对象时,都要调用release 释放对象,而 除此之外所得到的对象绝对不能释放,倘若在程序中释放了不是自己持有的对象,那么就会造成崩溃。

//例子 1
    id obj1 = [self allocObject]; //自己持有对象
    [obj1 release]; //对象已经释放了
    [obj1 release]; //释放已经非自己持有的对象时,即访问已经废弃的对象,程序崩溃
    
    //例子 2
    id obj2 = [self object]; //自己不持有对象,只是取得对象存在
    [obj2 release]; //释放非自己持有的对象,程序崩溃

这里的 allocObject 方法和 object 方法同上。

6、采用引用计数值进行内存管理的实现:

  • 在objective - c 的对象中存有引用计数。
  • 调用  retain 或者 alloc new  copy  mutableCopy 方法,引用计数值 +1;
  • 调用release 后,引用计数值 -1;
  • 引用计数值为0时,调用 dealloc 方法废弃对象。
7、autorelease 

autorelease 就是自动释放,它很类似与C语言中的自动变量(局部变量)的特性。

c语言中自动变量在程序执行过程中,若超出作用域,那么该自动变量被自动废弃。那么同理, autorelease  会像c语言中的自动变量那样来对待实例对象。当其超出作用域时, 对象实例的release方法会自动被调用。

使用方法如下:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //生成并持有NSAutoreleasePool对象
    id obj = [[NSObject alloc] init]; 
    [obj autorelease]; // 调用已经分配对象的 autorelease
    [pool drain]; // 废弃NSAutoreleasePool对象

最后一行的 [pool drain];  等同于  [poolrelease];


这篇文章也是参看 《objective-c高级编程  ios与os x多线程和内存管理》这本书时摘抄记录总结的。开始学习内存管理,这是第一篇记录。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值