在每个OC对象内部,都专门有8个字节的存储空间来存储引用计数器。 引用计数器的常见操作 retain消息:堆内存中对象的计数器变量 +1(该方法返回对象本身,要想计数器变量加1就要调用对象的retain方法) release消息: 堆内存中对象的计数器变量 -1(不代表释放对象,要想计数器变量减1就要调用对象的release方法) retainCount:获得对象当前的应用计数器值,输出:%ld %lu,NSLog(@"%lu",h.retainCount); 注意:release不代表销毁对象,仅仅是引用计数器-1 2.谁创建,谁release 1)如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者 autorelease方法. 2)不是你创建的就不用你去负责 3.谁retain,谁release 只要你调用了retain,无论这个对象时如何生成的,你都要调用release 5)对象的销毁 当1个对象的应用计数器为0时,那么它将被销毁,其占用的内存被系统回收。 当对象被销毁时,系统会自动向对象发送一条dealloc消息。一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的“临终遗言”。 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在代码块的最后调用。 注意:不能直接调用dealloc方法。 一旦对象被回收了,那么他所占用的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误) 注意; 1) 如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除 非整个程序已经退出 ) 2)任何一个对象,刚生下来的时候,引用计数器都为1。(对象一旦创建好,默认引用计数器就是1)当使用alloc、new或者copy创建一个对象时,对象的引用计数器默认就是1. 2、内存管理的分类 Objective-C提供了三种内存管理方式: 1.MannulReference-Counting(MRC,手动管理,在开发iOS4.1之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain、release、autorelease 等,而在其后 的版本可以使用 ARC,让系统自己管理内存。) 2.automatic reference-counting(ARC,自动引用计数,iOS4.1之后推出的) 3.garbage collection(垃圾回收)。iOS不支持垃圾回收; ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存; 开发中如何使用:需要理解MRC,但实际使用时尽量用ARC。
Gamer.h
#import <Foundation/Foundation.h> #import "House.h" @interface Gamer : NSObject { House *_house; // 房间,游戏玩家在哪里房间 } - (void)setHouse:(House *)house; - (House *)house; //@property House *house; @end
Gamer.m
#import "Gamer.h" @implementation Gamer - (void)dealloc { NSLog(@"玩家被释放"); // 当玩家自己被回收时,对房间进行一次release操作减少堆内存中对象的计数器变量值. [_house release]; [super dealloc]; } - (void)setHouse:(House *)house { if (_house != house) { //当玩家换房间时,需要对旧房间做一次release操作减少堆内存中对象的计数器变量值 [_house release]; // 玩家要进入房间,玩家就要对房间进行一次retain操作增加堆内存中对象的计数器变量值. _house = [house retain]; } } - (House *)house { return _house; } @end /* // 1、创建玩家对象 Gamer *game = [[Gamer alloc] init]; // 2、创建房间对象 Room *room = [[Room alloc] init]; // 3、让玩家进入房间 game.room = room;//堆中对象的计数器变量没有加1因为没有调用retain方法, // 4、释放房间对象 [room release]; // 5、输出玩家的房间 NSLog(@"玩家的房间是:%@",game.room); 此时,会报野指针错误。 解决方案: ,对房间进行一次retain。 // 声明类 @interface Gamer : NSObject { Room *_room; } - (void)setRoom:(Room *)room; - (Room *)room; // 实现类 @implementation Gamer - (void)setRoom:(Room *)room { [room retain];//堆中对象的计数器变量加1后赋值 _room = room; } - (Room *)room{ return _room; } 问题:房间无法被释放了怎么办? 解决方法: 在对象被释放时,要把该对象的所有对象类型的成员变量在dealloc当中进行释放: @implementation Gamer - (void)dealloc { [_room release];//堆中对象的计数器变量减1 NSLog(@"玩家被释放"); [super dealloc]; } @end 解决方案:判断新进房间与之前是否是同一个房间。 - (void)setRoom:(Room *)room { if (_room != room) { // 释放旧房间 [_room release]; [room retain];//加1后赋值 _room = room; } } 总的来说,有以下几点规律: 2)只要想要使用1个对象,那么就让对象的引用计数器+1。 3)当不再使用这个对象时,就让对象的引用计数器-1。 需求: 1、让玩家换房间,进入1号房间后,再进入2号房间 // 实现玩家类 @implementation Gamer - (void)setRoom:(Room *)room { [_room release];//旧房间的计数器变量减1 _room = [room retain];//新房间的计数器变量加1后赋值 } - (Room *)room{ return _room; } - (void)dealloc { [_room dealloc]; NSLog(@"玩家被释放"); [super dealloc]; } @end int main(){ // 1、创建玩家对象 Gamer *game = [[Gamer alloc] init]; // 2、创建房间对象 Room *room = [[Room alloc] init]; room.no = 1; Room *room1 = [[room alloc] init]; room.no = 2; // 3、让玩家进入房间 game.room = room; game.room = room1; // 4、释放房间对象 [room release]; // 5、释放玩家对象 [game release]; return 0; } 总结: 1、set方法的内存管理 - (void)setRoom:(Room *)room { if(_room != room){ [_room release]; _room = [room retain]; } } 2、dealloc方法内存管理 - (void)dealloc { // 当玩家不在了,代表不用房间了 // 对房间进行一次release [_room release]; // 重写dealloc方法,必须在最后调用父类的dealloc方法 [super dealloc]; }
House.h
#import <Foundation/Foundation.h> @interface House : NSObject @property int no; @end
Huose.m
#import "House.h" @implementation House - (void)dealloc { NSLog(@"%d房间被释放了",_no); [super dealloc]; } @end
main.m
/** 知识回顾: 什么是内存管理: 管理堆区的内存的分配和释放. 分配内存:new alloc copy 释放内存:release 操作内存的方式: 1)retainCount:获取对象的计数器的值 2)retain:堆内存中对象的计数器变量 +1 3)release:堆内存中对象的计数器变量 -1 僵尸对象:已经被释放的对象 野指针:指向僵尸对象的指针 空指针:nil,给空指针发送消息不会报错. 单个对象的内存管理 Person *p = [[Person alloc] init]; // 计数器 1 [p retain]; // 计数器 2 [p retain]; // 3 //想让p对象释放,怎么办? // 再做3次release [p release]; [p release]; [p release]; 一个对象是否被释放,通过什么方式确定? 通过是否调用dealloc方法,在重写dealloc方法时,必须在最后的位置调用[super dealloc]; 多对象内存管理: 1.模拟1个玩家进入1个房间的过程. 分析:类:玩家,房间类,让玩家拥有一间房 2.换房间的一个过程,玩家先进入10号房间,再进入20号房间.此时有什么问题? 解决方法: 在换房间时,需要release旧值,retain新值. */ #import <Foundation/Foundation.h> #import "Gamer.h" int main(int argc, const char * argv[]) { @autoreleasepool { Gamer *g = [[Gamer alloc] init]; //g 1 House *h = [[House alloc] init]; //h 1 //g.house = h只是表示h变量多了一个引用的指向,[[House alloc] init]对象任然只有一个引用,因为没有调用retain方法所以计数器变量不会加1. // g.house调用了g的setHouse方法所以此时[[House alloc] init]对象的引用为2 g.house = h; [h release]; // h 1 g.house = h; // 再重写进入房间 NSLog(@"%lu",h.retainCount); } return 0; } // 换房间 void demo() { Gamer *game = [[Gamer alloc] init]; // game :1 House *house = [[House alloc] init]; // house :1 house.no = 10; House *house2 = [[House alloc] init]; house2.no = 20; House *house3 = [[House alloc] init]; house3.no = 30; // 让人进入10号房 game.house = house; // 让人进入20号房间 game.house = house2; // 让人进入20号房间 game.house = house3; // 释放房间 [house release]; [house2 release]; [house3 release]; [game release]; }