obj-c本质就是"改进过的c语言",大家都知道c语言是没有垃圾回收(GC)机制的(注:虽然obj-c2.0后来增加了GC功能,但是在
iphone上不能用,因此对于iOS平台的程序员来讲,这个几乎没啥用),所以在obj-c中写程序时,对于资源的释放得由开发人员手动处理,相对要费
心一些。
引用计数
这是一种古老但有效的内存管理方式。每个对象(特指:类的实例)内部都有一个retainCount的引用计数,对象刚被创建时,retainCount为1,可以手动调用retain方法使retainCount+1,同样也可以手动调用release方法使retainCount-1,调用release方法时,如果retainCount值减到0,系统将自动调用对象的dealloc方法(类似于c#中的dispose方法),开发人员可以在dealloc中释放或清理资源。
1、基本用法
为了演示这种基本方式,先定义一个类Sample
类接口部分Sample.h
1 // 2 // Sample.h 3 // MemoryManage_1 4 // 5 // Created by jimmy.yang on 11-2-19. 6 // Copyright 2011 __MyCompanyName__. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface Sample : NSObject { 12 13 } 14 @end
类实现部分Sample.m
1// 2// Sample.m 3// MemoryManage_1 4// 5// Created by jimmy.yang on 11-2-19. 6// Copyright 2011 __MyCompanyName__. All rights reserved. 7// 8 9#import "Sample.h" 10 11@implementation Sample 12 13-(id) init 14{ 15 if (self=[super init]){ 16 NSLog(@"构造函数被调用了!当前引用计数:%d",[self retainCount]); 17 } 18 return (self); 19} 20 21-(void) dealloc{ 22 NSLog(@"析构函数将要执行...,当前引用计数:%d",[self retainCount]); 23 [super dealloc]; 24} 25@end
代码很简单,除了"构造函数"跟"析构函数"之外,没有任何其它多余处理。
主程序调用
1#import <Foundation/Foundation.h> 2#import "Sample.h" 3 4int main (int argc, const char * argv[]) { 5 6 Sample *_sample = [Sample new]; //构造函数被调用了!当前引用计数:1 7 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1 8 9 [_sample retain]; 10 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2 11 12 [_sample retain]; 13 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//3 14 15 [_sample release]; 16 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2 17 18 [_sample release]; 19 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1 20 21 [_sample release];//析构函数将要执行...,当前引用计数:1 22 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1,注:即便是在析构函数执行后,如果立即再次引用对象的retainCount,仍然返回1,但以后不管再试图引用该对象的任何属性或方法,都将报错 23 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//对象被释放之后,如果再尝试引用该对象的任何其它方法,则报错 24 //[_sample retain];//同上,会报错 25 26 return 0; 27}
这段代码主要验证:对象刚创建时retainCount是否为1,以及retain和release是否可以改变retainCount的值,同时retainCount减到0时,是否会自动执行dealloc函数
nil 的问题:
1.1 如果仅声明一个Sample类型的变量(其实就是一个指针),而不实例化,其初始值为nil
1.2 变量实例化以后,就算release掉,dealloc被成功调用,其retainCount并不马上回到0(还能立即调用一次且仅一次[xxx retainCount]),而且指针变量本身也不会自动归为nil值
1.3 dealloc被调用后,必须手动赋值nil,retainCount才会自动归0
以上结论是实际试验得出来的,见下面的代码:
1Sample *s ; 2NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0 3s = [Sample new]; 4NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1 5[s release]; 6NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1 7//NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//报错:Program received signal: “EXC_BAD_ACCESS”. 8s = nil; 9NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0
所以千万别用if (x == nil) 或 if ([x retainCount]==0)来判断对象是否被销毁,除非你每次销毁对象后,手动显式将其赋值为nil
2、复杂情况
上面的示例过于简章,只有一个类自己独耍,如果有多个类,且相互之间有联系时,情况要复杂一些。下面我们设计二个类Shoe和Man(即“鞋子类”和”人“),每个人都要穿鞋,所以Man与Shoe之间应该是Man拥有Shoe的关系。
Shoe.h接口定义部分
1 #import <Foundation/Foundation.h> 2 3 4 @interface Shoe : NSObject { 5 NSString* _shoeColor; 6 int _shoeSize; 7 } 8 9 //鞋子尺寸 10 -(void) setSize:(int) size; 11 -(int) Size; 12 13 //鞋子颜色 14 -(void) setColor:(NSString*) color; 15 -(NSString*) Color; 16 17 //设置鞋子的颜色和尺码 18 -(void) setColorAndSize:(NSString*) pColor shoeSize:(int) pSize; 19 20 @end
Shoe.m实现部分
1 // 2 // Shoe.m 3 // MemoryManage_1 4 // 5 // Created by jimmy.yang on 11-2-19. 6 // Copyright 2011 __MyCompanyName__. All rights reserved. 7 // 8 9 #import "Shoe.h" 10 11 12 @implementation Shoe 13 14 //构造函数 15 -(id)init 16 { 17 if (self=[super init]){ 18 _shoeColor = @"black"; 19 _shoeSize = 35; 20 } 21 NSLog(@"一双 %@ %d码 的鞋子造好了!",_shoeColor,_shoeSize); 22 return (self); 23 } 24 25 -(void) setColor:(NSString *) newColor 26 { 27 _shoeColor = newColor; 28 } 29 30 -(NSString*) Color 31 { 32 return _shoeColor; 33 } 34 35 -(void) setSize:(int) newSize 36 { 37 _shoeSize = newSize; 38 } 39 40 -(int) Size 41 { 42 return _shoeSize; 43 } 44 45 -(void) setColorAndSize:(NSString *)color shoeSize:(int)size 46 { 47 [self setColor:color]; 48 [self setSize:size]; 49 } 50 51 52 //析构函数 53 -(void) dealloc 54 { 55 NSLog(@"%@ %d码的鞋子正在被人道毁灭!",_shoeColor,_shoeSize); 56 [super dealloc]; 57 } 58 59 60 @end
Man.h定义部分
1 // 2 // Man.h 3 // MemoryManage_1 4 // 5 // Created by jimmy.yang on 11-2-20. 6 // Copyright 2011 __MyCompanyName__. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Shoe.h" 11 12 13 @interface Man : NSObject { 14 NSString *_name; 15 Shoe *_shoe; 16 } 17 18 19 -(void) setName:(NSString*) name; 20 -(NSString*)Name; 21 22 -(void) wearShoe:(Shoe*) shoe; 23 @end
Man.m实现部分
1 // 2 // Man.m 3 // MemoryManage_1 4 // 5 // Created by jimmy.yang on 11-2-20. 6 // Copyright 2011 __MyCompanyName__. All rights reserved. 7 // 8 9 #import "Man.h" 10 11 12 @implementation Man 13 14 //构造函数 15 -(id)init 16 { 17 if (self=[super init]){ 18 _name = @"no name"; 19 } 20 NSLog(@"新人\"%@\"出生了!",_name); 21 return (self); 22 } 23 24 25 -(void) setName:(NSString *)newName 26 { 27 _name =newName; 28 } 29 30 -(NSString*)Name 31 { 32 return _name; 33 } 34 35 -(void) wearShoe:(Shoe *)shoe 36 { 37 _shoe = shoe; 38 } 39 40 //析构函数 41 -(void) dealloc 42 { 43 NSLog(@"\"%@\"要死了! ",_name); 44 [super dealloc]; 45 } 46 47 @end
main函数调用
1#import <Foundation/Foundation.h> 2#import "Shoe.h" 3#import "Man.h" 4 5int main (int argc, const char * argv[]) { 6 7 Man *jimmy = [Man new]; 8 [jimmy setName:@"Jimmy"]; 9 10 11 Shoe *black40 =[Shoe new]; 12 [black40 setColorAndSize:@"Black" shoeSize:40]; 13 14 [jimmy wearShoe:black40]; 15 16 [jimmy release]; 17 [black40 release]; 18 19 return 0; 20 21}
2011-02-23 13:05:50.550 MemoryManage[253:a0f] 新人"no name"出生了! 2011-02-23 13:05:50.560 MemoryManage[253:a0f] 一双 black 35码 的鞋子造好了! 2011-02-23 13:05:50.634 MemoryManage[253:a0f] "Jimmy"要死了! 2011-02-23 13:05:50.636 MemoryManage[253:a0f] Black 40码的鞋子正在被人道毁灭!
以上是输出结果,一切正常,jimmy与black40占用的资源最终都得到了释放。但是有一点不太合理,既然鞋子(black40)是属于人
(jimmy)的,为什么人死了(即:[jimmy
release]),却还要main函数来责任烧掉他的鞋子?(即:main函数中还是单独写一行[black40 release])
貌似人死的时候,就连带自上的所有东西一并带走,这样更方便吧。
ok,我们来改造一下Man.m中的dealloc()方法,改成下面这样:
1//析构函数 2-(void) dealloc 3{ 4 NSLog(@"\"%@\"要死了! ",_name); 5 [_shoe release];//这里释放_shoe 6 [super dealloc]; 7}
即:在Man被销毁的时候,先把_shoe给销毁。这样在main()函数中,就不再需要单独写一行[black40 release]来释放black40了.
现在又有新情况了:jimmy交了一个好朋友mike,二人成了铁哥们,然后jimmy决定把自己的鞋子black40,跟mike共同拥有,于是main函数就成了下面这样:
1int main (int argc, const char * argv[]) { 2 3 Man *jimmy = [Man new]; 4 [jimmy setName:@"Jimmy"]; 5 6 Shoe *black40 =[Shoe new]; 7 [black40 setColorAndSize:@"Black" shoeSize:40]; 8 9 [jimmy wearShoe:black40]; 10 11 Man *mike = [Man new]; 12 [mike setName:@"mike"]; 13 [mike wearShoe:black40];//mike跟jimmy,现在共同拥有一双40码黑色的鞋子 14 15 [jimmy release]; 16 [mike release]; 17 18 return 0; 19
|
请发表评论