Objective-C边学边记-12:Cocoa内存管理

注:本章对初学者比较晦涩,建议参考或直接阅读英文原本来理解。

 

1.对象生命周期
对象的生命周期包括诞生(通过alloc或new方法实现)、生存(接收消息和执行操作)、交友(借助方法的组合和参数)、释放(当它们的生命结束时最终死去)。当对象的生命周期结束时,它们的原材料(内存)将被回收以供新的对象使用。

引用计数(reference counting):每个对象有一个与之相关联的整数,称做它的引用计数器或保留计数器。当某段代码需要访问一个对象时,该代码将该对象的保留计数器值加1,表示“我要访问该对象”。当这段代码结束对象访问时,将对象的保留计数器值减1,表示它不再访问该对象。当保留计数器值为0时,表示不再有代码访问该对象了,因此对象将被销毁,其占用的内存被系统回收以便重用。

当使用alloc、new或copy(生成接收对象的一个副本)创建一个对象时,对象的保留计数器值被设置成1.要增加对象的保留计数器值,可以给对象发送一条retain消息,要减少对象的保留计数器值,可以给对象发送一条release消息。

当一个对象因其保留计数器归0而将被销毁时,Objective-C自动向对象发送一条dealloc消息。你可以在自己的对象中重写dealloc方法。可以通过这种方式释放已经分配的全部相关资源。一定不要直接调用dealloc方法。要获得保留计数器的当前值,可以发送retainCount消息。签名如下:

  
    
- (id) retain; // 申请
- ( void ) release; // 释放
- (unsigned) retainCount; // 当前计数器值


例如:[[car retain] setTire: tire atIndex:2];  表示要求car对象将其保留计数器值加1并执行setTire操作。
Example:

 

  
    
1 //
2   // Main.m
3   // RetainCount1
4   // 内存管理练习1
5   //
6   // Created by Elf Sundae on 10/23/10.
7   // Copyright 2010 Elf.Sundae(at)Gmail.com. All rights reserved.
8   //
9  
10 #import < Cocoa / Cocoa.h >
11
12 @interface RetainTracker : NSObject
13
14 @end
15
16 @implementation RetainTracker
17
18   - (id) init
19 {
20 if (self = [super init])
21 {
22 NSLog( @" init: Retain count of %d. " ,[self retainCount]);
23 }
24 return self;
25 }
26
27   - ( void ) dealloc
28 {
29 NSLog( @" dealloc called.Bye Bye. " );
30 [super dealloc];
31 }
32
33 @end
34
35
36
37   int main ( int argc, const char * argv[])
38 {
39
40
41 RetainTracker * tracker = [RetainTracker new ];
42
43 [tracker retain];
44 NSLog( @" %d " ,[tracker retainCount]);
45
46 [tracker retain];
47 NSLog( @" %d " ,[tracker retainCount]);
48
49 [tracker release];
50 NSLog( @" %d " ,[tracker retainCount]);
51
52
53 [tracker release];
54 NSLog( @" %d " ,[tracker retainCount]);
55
56 [tracker retain];
57 NSLog( @" %d " ,[tracker retainCount]);
58
59 [tracker release];
60 NSLog( @" %d " ,[tracker retainCount]);
61
62 [tracker release];
63
64
65 return 0 ;
66 }
67  

 

 

 

输出:

init: Retain count of 1.
2
3
2
1
2
1
dealloc called.Bye Bye.

 

2.对象所有权(object ownership)
如果一个对象具有指向其他对象的实例变量,则称该对象拥有这些对象。这意味着该实体要负责确保对其拥有的对象进行清理。
当多个实体拥有某个特定对象时,对象的所有权关系就更复杂了,这也是保留计数器值可能大于1的原因。在上面的RetainCount1例子中,main()函数拥有RetainTracker类的对象,因此main()要负责清理该类的对象。
回忆一下在OOP基础章节里Car的engine setter方法:

  
    
- ( void ) setEngine: (Engine * ) newEngine;

以及如何在main()函数中调用该方法:

  
    
Engine * engine = [Engine new ];
[car setEngine: engine];

现在哪个实体拥有engine对象?是main()函数还是Car类?

 


下面是编写setEngine的一种更好的方法:

 

  
    
1 - ( void ) setEngine: (Engine * ) newEngine
2 {
3 [newEngine retain];
4 [engine release];
5 engine = newEngine;
6 } // setEngine

在访问方法中,如果先保留新对象,然后再释放旧对象,这不会出问题。
In your accessors, if you retain the new object before you release the old object, you’ll be safe.

 

 

3.自动释放(Autorelease)
自动释放池(autorelease pool),它是一个存放实体的池(集合),这些实体可能是对象,能够被自动释放。
NSObject类提供了一个autorelease方法:

  
    
- (id) autorelease;

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

 

 

  
    
1 - (NSString * ) description
2 {
3 NSString * description;
4 description = [[NSString alloc]
5 initWithFormat: @" I am %d years old " , 4 ];
6 return ([description autorelease]);
7 } // description

 

 

自动释放池的销毁时间:
在使用Foundation库工具中,创建和销毁自动释放池的方法非常明确:

  
    
NSAutoreleasePool * pool;
pool
= [[NSAutoreleasePool alloc] init];

[pool release];

 

 

创建一个自动释放池时,该池自动成为活动的池。释放该池时,其保留计数器值归0,然后该池被销毁。在销毁过程中,该池释放其包含的所有对象。
当时用AppKit时,Cocoa定期自动为你创建和销毁自动释放池。通常是在程序处理完当前事件(如鼠标单击或按键)以后执行这些操作。你可以使用任意多的自动释放对象,当不再使用它们时,自动释放池将自动为你清理这些对象。
说明:在Xcode自动生成的代码中使用了另一种销毁自动释放池中对象的方式:-drain方法,该方法只是清空自动释放池而不销毁它。-drain方法只适用于Mac OS X 10.4(Tiger)及更高版本。

 

自动释放池的工作过程:
示例:RetainCount2

OBJECTIVE-C CODE   :RetainCount2
 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63
//

//  Main.m

//  RetainCount2

//  内存管理练习2: 使用自动释放池

//

//  Created by Elf Sundae on 10/25/10.

//  Copyright 2010 Elf.Sundae(at)Gmail.com. All rights reserved.

//



#import <Cocoa/Cocoa.h>



@interface RetainTracker : NSObject



@end



@implementation RetainTracker



- (id) init

{

	if (self = [super init])

	{

		NSLog(@"init: Retain count of %d.",[self retainCount]);

	}

	return self;

}



- (void) dealloc

{

	NSLog(@"dealloc called.Bye Bye.");

	[super dealloc];

}



@end







int main (int argc, const char * argv[]) 

{

	// 创建一个自动释放池

	NSAutoreleasePool *pool;

	pool = [[NSAutoreleasePool alloc] init];

	

	// 创建(new)一个RetainTracket对象

	RetainTracker *tracker = [RetainTracker new]; // count:1

	NSLog(@"%d",[tracker retainCount]);

	

	// 处于演示目的,retain一个trancker

	[tracker retain]; // count:2

	NSLog(@"%d",[tracker retainCount]);

	

	// 向对象发送autorelease消息,将该对象添加到自动释放池pool中

	[tracker autorelease]; // count: still 2

	NSLog(@"%d",[tracker retainCount]);

	

	// release该对象以抵消上面的 retain

	[tracker release]; //count: 1

	NSLog(@"%d",[tracker retainCount]);

	

	NSLog(@"releaseing pool");

	[pool release];

	

	return 0;

}

  

你可能感兴趣的:(Objective-C)