【object-c 学习笔记】第9章 内存管理

1、概念理解

举例:以图书馆为例,如果每个人都只借不还,那么图书馆最终将会因无书可借而倒闭。

概念:当程序运行结束时,操作系统将收回占用的资源,但是只要程序还在运行,它会一直占用资源,如果不进行清理,某些资源将被耗尽,程序有可能会崩溃,内存管理就是确保在需要的时候分配内存,不需要使用的时候释放内存

2、对象生命周期

  包含诞生(通过alloc或者new方法实现)、生存(接受信息并执行操作)、交友(通过复合以及向方法传递参数)、以及死去(释放内存)。

3、引用计数

  cocoa采用引用计数了解对象的生命周期是否结束了。

(1)计数方法

 a.计数器值加1,表示某段代码需要访问一个对象;

 b.计数器值减1,表示某段代码结束访问一个对象;

 c.计数器值为0,表示不再有代码访问该对象了,因此它将销毁,其占用的内存被系统回收以便重用。

 (2)使用方法

 a. 当计数器值为0时,object-c会自动向对象发送一条dealloc消息;可以重写dealloc方法,把已经分配的全部相关资源释放。

   注意:一定不要直接调用dealloc方法,,object-c会自动销毁对象时自动调用它。

 b.当需要获得引用计数器当前的值,可以发送retainCount消息。

//retain、release、retainCount的方法申明
1 -(id)retain;
2 - (oneway void) release;
3 - (NSUInteger) retainCount;
  retain方法返回一个id类型的值,通过这种方式,可以在接受其他消息的同时进行retain调用,增加对象的保留计数器的值和傲气对象完成某种操作。例如:
[[car retain]setTire:atIndex:2]; 表示要求car对象将其保留计数器的值加1并执行setTire操作。

(3)案例
a.创建一个RetainTracker类的对象,该对象在初始化和销毁时调用了NSLog()函数,当对象的保留计数器的值归0时,将自动发送dealloc消息,例子中,init和dealloc这两个方法使用NSLog()输出一条信息,表明他们被调用了。
 1 @interface RetainTracker : NSObject
 2 @end//RetainTracker
 3 
 4 @implemetation RetainTracker
 5 -(id) init
 6 {
 7    if (self = [super init])
 8    { 
 9        NSLog(@"init : Reetain count of %d." , [self retainCount]);
10    }
11    return(self);
12 }//init
13 
14 -(void)dealloc
15 {
16    NSLog(@"dealloc called. Bye Bye.");
17    [super dealloc];
18 }//dealloc
19 @end//RetainTracker

  在main()函数中创建了一个新的RetainTracker类的对象,并间接调用了由Retain-Tracker类定义的两个方法。当一个新的RetainTracker类的对象创建完毕后,就会向它发送retain消息或release消息,以增加和减少对象的保留计数器的值,以下是NSLog()函数有趣的输出结果。

 1 int  main(int argc,const char  *argv[])
 2 {
 3    RetainTracker *tracker = [RetainTracker new]; //count:1
 4 
 5    [tracker retain];  //count:2
 6    NSLog(@"%d",[tracker retainCount]);
 7 
 8  
 9    [tracker retain];  //count:3
10    NSLog(@"%d",[tracker retainCount]);
11 
12    [tracker release];  //count:2
13    NSLog(@"%d",[tracker retainCount]);
14 
15    [tracker release];  //count:1
16    NSLog(@"%d",[tracker retainCount]);
17 
18    [tracker retain];  //count:2
19    NSLog(@"%d",[tracker retainCount]);
20 
21   [tracker release];  //count:1
22    NSLog(@"%d",[tracker retainCount]);
23 
24   [tracker release];  //count:1
25    return(0);
26 }//main

运行后结论:

-------------------------------------------------------------------------------------------------------------------------------------------

init Retain count of 1.

2

3

2

1

2

1

dealloc called. Bye Bye.

-------------------------------------------------------------------------------------------------------------------------------------------

4、对象所有权

  回忆下,car类的变量engine的存取方法:

1 //car类的变量engine的存取方法
2 -(void) setEngine : (Engine *) newEngine;
3 
4 //在main()函数中调用该方法:
5 Engine *engine = [Engine new];
6 [car setEngine : engine];

  那么那个实体拥有engine对象?

  car类正在使用engine底线,所以不可能是main()函数;main()随后可能会使用engine对象,也不可能是car的;解决办法是car类保存engine对象,将engine对象的保留计数器的值增加到2.

5、访问方法中的保留和释放

  setEngine方法的第一个内存管理版本

1 -(void)setEngine : (Engine *)newEngine
2 {
3    [newEngine retain];
4    [engine release];
5    engine = [newEngine retain];
6 }//setEngine
7 //首先保留新的engine对象,即使newEngine和engine是同一个对象,保留计数器的值也会先增加,然后立即减少,由于没有归0,engine对象也没被销毁,就不会引发错误了。
8 //假设先保留新对象,在释放对象就会出问题。

6、自动释放

  description方法的例子(该方法返回一个用来描述对象信息NSString类型值)

 1 -(NSString *) descrinption
 2 {
 3    NSString *description;
 4    description = [[NSString alloc] initWithFormat: @"I am %d years old",4];
 5 
 6    return(description);
 7 }//description
 8 //使用alloc 方法创建一个新的字符串实例(alloc方法将该对象的保留计数器的值设置为1),然后返回该字符串实例。
 9 //调用description方法的代码将返回的字符串赋在某个变量中,并在使用完毕后将其释放
10 NSString *desc = [someObject description];
11 NSLog(@"%@",desc);
12 [desc release];

7、所有对象放入池中

  Cocoa中有一个自动释放池(autorelease pool)概念:用来存储对象的集合,并且能够自动释放。

  NSObject类提供一个叫autorelease的方法:

  -(id)autorelease;

  该方法预先设定了一条在未来某个时间发送的release消息,其返回是接收这条信息的对象,当给一个对象发送autorelease消息时,实际上是该对象添加到了自动释放池中,当自动释放池被销毁是,会像该池中的所以对象发送release消息。

-(id)autorelease;

-(NSString *) descrinption
{
    NSString *description;
    description = [[NSString alloc] initWithFormat: @"I am %d years old",4];
 
    return(description);
 }//description

NSLog(@"%@", [description autorelease] );
//因为description方法首先创建了一个新的字符串对象,然后自动释放该对象最好将其返回给NSLog()函数,所以内存管理问题解决了。由于description方法中的字符串对象是自动释放的,该对象暂时被放入了当前活动的自动释放池中,等到调用NSLog()函数的代码结束后,自动释放池会被自动销毁。

8、自动释放池的销毁时间

  介绍2种方法创建一个自动释放池

  1、通过@autoreleaspool关键字

  2、通过NSAutoreleasepool对象

  

  在Foundation库工具集中,创建和销毁自动释放池由@autorelease关键字完成。当你使用@autorelease{}时,所有在花括号里的代码都会被放入这个新的池子中,如果你的程序是密集型的可以使用这种自动释放池。

  注意:任何在花括号里定义的变量在括号外都无法使用;使用NSAutoleasePool对象,如果你使用了这个方法,创建和释放NSAutoreleasePool对象之间的代码就会使用这个新的池子。

NSAutoreleasePool *pool;
pool = [NSAutoreleasePool new];
~
[pool release];
//创建一个自动释放池后,该池子就会成为活动的池子,释放该池后,其保留计数器的值归0,然后该池被销毁,销毁过程中,该池将释放其包含的所有对象

9、自动释放池的工作流程

 1 int main(int argc , const char *argv[])
 2 {
 3 //创建自动释放池
 4    NSAutoreleasePool *pool;
 5    pool = [[NSAutoreleasePool alloc] init ];
 6    
 7 //每次向某对象发送autorelease消息,该对象会被添加到自动释放池中
 8    RetainTracker *tracker;
 9    tracker = [[RetainTracker new]];//count:1
10 
11 //创建一个新的tracker对象,因为创建时接收了一条new消息,所以保留计数器值为1,接下来保留该对象,保留计数器增加到2
12    [tracker retain];//count:2
13 //该对象被自动释放,但其保留计数器值仍然不变;
14    [tracker autorelease]; //count:still 2
15 //注意我们之前创建的自动释放池中现有一个引用指向该对象,当自动释放池被销毁时,将tracker对象发送一条release消息
16    [tracker release];//count:1
17 //之后释放该对象以抵消之前对他执行的保留操作,该对象的保留计数器值仍大于0,所以处于激活状态
18    NSLog(@"releasing pool");
19    [pool release];
20 
21    @autoreleasepool
22    {
23        RetainTracker *tracker2;
24        tracker2 = [RetainTracker new]; //count:1
25        [tracker2 retain];//count:2
26        [tracker2 autorelease]; //count:still 2
27        [tracker2 release];//count:1
28 
29        NSLog(@"auto releasing pool");
30    }
31 
32    return(0);
33 }//main
原文地址:https://www.cnblogs.com/sallyWei/p/4498843.html