iOS内存管理
例子1——QQ堂游戏
①A玩家,建立房间1 ————————房间1的人数为1
②B玩家,加入房间1 ————————房间1的人数为2
③游戏结束,B玩家离开房间1 ———— 房间1的人数为1
④A玩家,也离开房间1 ——————— 房间1的人数为0
分析:当A玩家创建了房间1时,房间的人数变为1,随着玩家B的加入,房间的人数变为2
当游戏结束,B玩家离开房间1,此时人数变为1,但是房间1还在
最后当房间A离开房间1的时候,人数表位0,此时系统会将房间1释放
这就是内存管理的基本原则,通过对象内存计数器对对象所占用的内存进行管理,当有变量或者对象占用这块内存的时候,计数器加1,当某个变量或者对象不占占用时,计数器减1。当对象内存计数器变为0时,该对象释放。
例子2——人和书的关系
①创建书对象———————— 内存计数器加1
②创建人对象———————— 内存计数器加1
③人可以拥有书———————书内存计数器加1
④人不再拥有书——————— 书内存计数器减1
注意
Xcode默认会开启自动内存管理ARC机制(由iOS系统自动对内存进行管理),在学习内存管理中,我们需要将ARC管理机制关闭,否则,我们无法看到内存的管理的细节,诸如alloc \dealloc \release等方法无法重写
关闭方式
创建工程后,在Build Settings中搜索garbage,将ARC关闭即可,如下图:
接下来,我们进入代码的实现过程,看Objective-C是如何对内存进行管理的,仍然以人和书为例
/****************
1、创建Person类和Book类
2、分别创建对象
3、将Book对象赋给Person对象的_book属性
4、重写dealloc方法
5、观察对象的计数器变化
****************/
- #import <Foundation/Foundation.h>
- @interface Book : NSObject
- {
- int _price; //属性,价格
- }
- - (void)setPrice:(int)price;
- - (int)price;
- @end
- @implementation Book
- - (void)setPrice:(int)price
- {
- _price = price;
- }
- - (int)price
- {
- return _price;
- }
- @end
- @interface Person : NSObject
- {
- Book *_book; //人拥有一本书
- }
- - (void)setBook:(Book *)book;
- - (Book *)book;
- @end
- @implementation Book
- - (void)setBook:(Book *)book
- {
- _book = book;
- }
- - (Book *)book
- {
- return _book;
- }
- @end
- int main()
- {
- Book *b = [[Book alloc]init];
- Person *p =[[Person alloc]init];
- [p setBook:b];
- [p release];
- [b release];
- }
上述代码中,由于关闭了ARC自动内存管理机制,所以按照上述的写法,会产生内存泄露
分析如下:
首先,执行main函数后,在内存中的分布如下。
根据内存管理的原理,
执行第43行代码的时候,在内存中创建Book对象,此时Book对象内存计数器为1
执行第43行代码的时候,在内存中创建Person对象,此时Person对象内存计数器为1
当47行代码注释后,执行到48行代码时,Book对象的内存计数器减1变为0,此时Book对象被释放,此时p对象的_book属性仍然占用Book对象,会导致_book会出现内存泄露,所以需要进行内存管理
内存管理的原理是:谁占用,就需要对计数器加1(retain);解除占用后,需要对计数器减1(release)。
所以,上述代码中,需要做如下更改
①当执行45行代码的时,会调用Person对象的set方法,Book对象会被赋给_book属性,此时_book需要对Book对象计数器加1,所以setBook方法需要更改如下:
- (void)setBook:(Book *)book
{
_book = [book retain]; //通过retain对计数器加1,且retain方法返回自身,所以仍然是将book赋值给_book.
}
②为了验证Book对象和Person对象是否被释放,我们分别在.m文件中(18行和38行后)重写dealloc方法
注意,重写dealloc方法时,一定要在末尾调用[super dealloc]
- (void)dealloc
{
NSLog(@“Book对象被释放");
[super dealloc];
}
- (void)dealloc
{
NSLog(@“Person对象被释放”);
[super dealloc];
}
在重写了dealloc方法后,运行后的结果如下:
从运行结果中,我们可以看到,只有Person对象被释放,Book对象并未被释放。原因如下
当执行第45行代码的时候,调用了set方法,此时对Book进行了retain,计数器变为2。在48行的时候,计数器减1变为1,没有被销毁,没有调用dealloc方法。
所以,遵循内存管理原则(谁retain,谁release),Person对象需要解除对Book的占用,所以在Person对象被销毁之前,进行此项操作,故Person对象的dealloc方法需要被重写为如下:
- (void)dealloc
{
[_book release]; //对让Book对象的内存计数器减1
NSLog(@“Person对象被释放”);
[super dealloc];
}
③通过上述更改,执行了47行和48行代码后,Book对象和Person对象被销毁。此时p和b指针指向了一块被释放的内存,如果再通过p和b操作该内存,会出现报错(野指针错误),所以需要分别在47行和48行后面分别加上如下代码:
p = nil;
b = nil;
p和b被清空,空指针不会报错。
更改后的代码代码整体如下:
- #import <Foundation/Foundation.h>
- @interface Book : NSObject
- {
- int _price; //属性,价格
- }
- - (void)setPrice:(int)price;
- - (int)price;
- @end
- @implementation Book
- - (void)setPrice:(int)price
- {
- _price = price;
- }
- - (int)price
- {
- return _price;
- }
- - (void)dealloc
- {
- NSLog(@“Book对象被释放");
- [super dealloc];
- }
- @end
- @interface Person : NSObject
- {
- Book *_book; //人拥有一本书
- }
- - (void)setBook:(Book *)book;
- - (Book *)book;
- @end
- @implementation Book
- - (void)setBook:(Book *)book
- {
- _book = [book retain]; //Person对象占用书这个对象时,需要对计数器加1
- }
- - (Book *)book
- {
- return _book;
- }
- - (void)dealloc
- {
- [_book release]; //Book对象被释放
- NSLog(@“Person对象被释放”);
- [super dealloc];
- }
- @end
- int main()
- {
- Book *b = [[Book alloc]init];
- Person *p =[[Person alloc]init];
- [p setBook:b];
- [p release];
- p = nil;
- [b release];
- p = nil;
- }
更多iOS开发相关资讯,欢迎关注“一路上有你”