一、为什么要内存管理
移动设备的内存极其有限,每个app所能占用的内存是有限制的,如果app占用过多内存,应用程序也会不能继续运行。如果内存管理不当,也会出现空指针,野指针,僵尸对象等错误。在OC中,如果没有开启ARC,对任何继承了NSObject的对象,都要进行内存管理。系统会对其他基本数据类型(int、char、float、double、struct、enum等)自动进行内存管理。
二、内存管理的原理
在每个对象内部,都有一个4个字节的引用计数器,表示“对象的引用个数”,当时用new,alloc,copy创建一个新对象后,计数器的值为1。当对象调用retain方法时,计数器加1,当调用release方法是,计数器减1。学过操作系统的人应该会知道,这两个方法和操作系统进程管理的PV操作很像,有可能发明OC语言的人写内存管理是借鉴操作系统中的进程管理呢,呵呵。当计数器的值减为0后,对象会被回收。在对象被销毁之前,系统会想对象发送一条dealloc消息。
三、关闭ARC
ARC(Autom Reference Counting)是IOS5的新功能,会自动帮我们实现对象的内存管理,并且不能调用手动管理内存的方法。为了学习内存管理,我们必须关闭ARC,手动实现内存管理。
点击工程->build setting->all,在搜索框中输入auto,找到Object-C Automatic Reference Counting,把YES改成NO,就关闭了这个项目的ARC功能。
四、内存管理的原则
1. 谁创建,谁release
如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease
2. 谁retain,谁release
只要你调用了retain,无论这个对象是如何生成的,你都要调用release
五、多对象内存管理
新建一个控制台工程,在工程中添加Person类和 book类
bookh
#import <Foundation/Foundation.h>
@interface<a target=_blank class="izllvbhl" href="#" title="Click to Continue > by MacVx"> book<img src="https://i-blog.csdnimg.cn/blog_migrate/967e5b9afc9c7c88a4dc8436fa94f9ac.png" alt="" /></a> : NSObject
{
int _price;
}
- (void)setPrice:(int)price;
- (int)price;
@end
bookm
#import <a target=_blank class="izllvbhl" href="#" title="Click to Continue > by MacVx"> book<img src="https://i-blog.csdnimg.cn/blog_migrate/967e5b9afc9c7c88a4dc8436fa94f9ac.png" alt="" /></a>.h"
@implementation Book
- (void)setPrice:(int)price
{
_price = price;
}
- (int)price
{
return _price;
}
-(void)dealloc
{
NSLog(@"Book对象被回收!");
[super dealloc];
}
@end
Person.h
#import <Foundation/Foundation.h>
#import <a target=_blank class="izllvbhl" href="#" title="Click to Continue > by MacVx"> book<img src="https://i-blog.csdnimg.cn/blog_migrate/967e5b9afc9c7c88a4dc8436fa94f9ac.png" alt="" /></a>.h"
@interface Person : NSObject
{
<a target=_blank class="izllvbhl" href="#" title="Click to Continue > by MacVx"> book<img src="https://i-blog.csdnimg.cn/blog_migrate/967e5b9afc9c7c88a4dc8436fa94f9ac.png" alt="" /></a> *<a target=_blank class="izllvbhl" href="#" title="Click to Continue > by MacVx"> book<img src="https://i-blog.csdnimg.cn/blog_migrate/967e5b9afc9c7c88a4dc8436fa94f9ac.png" alt="" /></a>;
}
- (void)setBook:<a target=_blank class="izllvbhl" href="#" title="Click to Continue > by MacVx"> book<img src="https://i-blog.csdnimg.cn/blog_migrate/967e5b9afc9c7c88a4dc8436fa94f9ac.png" alt="" /></a> *<a target=_blank class="izllvbhl" href="#" title="Click to Continue > by MacVx"> book<img src="https://i-blog.csdnimg.cn/blog_migrate/967e5b9afc9c7c88a4dc8436fa94f9ac.png" alt="" /></a>;
- (Book *)book;
@end
Person.m
#import "Person.h"
@implementation Person
- (void)setBook:(Book *)book
{
_book = book;
}
- (Book *)book
{
return _book;
}
-(void)dealloc
{
NSLog(@"Person对象被回收!");
[super dealloc];
}
@end
在main函数中创建Person对象和Book对象,现在想让Person对象拥有Book对象,依照我们刚才说的原则,代码很容易写。
#import <Foundation/Foundation.h>
#import "Book.h"
#import "Person.h"
int main(int argc, const char * argv[])
{
Book *b = [[Book alloc] init];
Person *p = [[Person alloc] init];
p.book = b;
[p release];
[b release];
return 0;
}
运行结果好像也没问题。
但是,如果我们将最后的两个release调换位置,仔细分析一下会发现当先运行了[b realease]后,Book对象就被销毁了,但是p对Book对象还有一个引用,这样是不合理的!
分析后发现,在p.book = b; 我们并没有让b的引用计数器加1。所以,在Person的set方法中,将_book = book;改成_book = [book retain];把retain之后的返回的book对象赋值给_book。
运行程序
只有Person对象被回收了!
回想内存管理的原则,有了retain就必须有release,但是我们在set方法中retain之后没有realease。所以,还要在Person的dealloc方法中加入[_book realase];程序才能运行成功。