iOS —— 简述OC 的内存管理机制
关于OC 的内存管理有很多模糊的地方,下面我们一一说起
首先概念性问题
概念
为什么进行内存管理
由于移动设备的内存*其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等。
内存管理类型: OC 类型
基本类型和C语言的类型:如:
int,short,char,struct,enum,union等类型
OC类型:任何继承于NSObject对象都属于OC的类型。
我们讲的内存管理实际上是对OC类型的内存管理,它对基本数据类型和C语言的类型并不管用。
原因: 因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。
对象的基本结构
每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象。对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁。
在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器。
管理方法
Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池.
引用计数器的作用
判断对象要不要回收的唯一依据就是计数器是否为0,若不为0则存在。
对象的销毁
当一个对象的引用计数器为0时,那么它将被销毁,其占用的内存被系统回收。
当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的“临终遗言”。一旦重写了dealloc方法就必须调用[super dealloc],并且放在代码块的*后调用(不能直接调用dealloc方法)。
一旦对象被回收了,那么他所占据的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误)。
内存管理原则
OC使用了一种叫做引用计数的机制来管理对象,如果对一个对象使用了alloc、[Mutable]copy,retain,那么你必须使用相应的realease或者autorelease。
也可以理解为自己生成的对象,自己持有。
非自己生成的对象,自己也能持有。
不在需要自己持有的对象时释放。
非自己持有的对象自己无法释放。
生成并持有对象,持有对象,释放对象,废弃对象。
MRC 手动内存计数
(Reference Counted)手动内存计数:就是说,从一段内存被申请之后,就存在一个变量用于保存这段内存被使用的次数,我们暂时把它称为计数器,当计数器变为0的时候,那么就是释放这段内存的时候。
例如:
当在程序A里面一段内存被成功申请完成之后,那么这个计数器就从0变成1(我们把这个过程叫做alloc)
然后程序B也需要使用这个内存,那么计数器就从1变成了2(我们把这个过程叫做retain)。
紧接着程序A不再需要这段内存了,那么程序A就把这个计数器减1(我们把这个过程叫做release);
程序B也不再需要这段内存的时候,那么也把计数器减1(这个过程还是release)。
当系统(也就是Foundation)发现这个计数器变成了0,那么就会调用内存回收程序把这段内存回收(我们把这个过程叫做dealloc)。顺便提一句,
注意
如果没有Foundation,那么维护计数器,释放内存等等工作需要你手工来完成。**
一般是由类的静态方法创建的, 函数名中不会出现alloc或init字样, 如[NSString string]和[NSArray arrayWithObject:], 创建后引用计数+0(autoRealease), 在函数出栈后释放, 即相当于一个栈上的局部变量. 当然也可以通过retain延长对象的生存期.
MRC下内存管理代码规范
只要调用了alloc,就必须有release(autorelease)
get 方法的代码规范
-(NSString *)value{
return [[_value retain] autorelease];
}
}
set 方法的代码规范
基本数据类型:直接赋值
-(void)setAge:(int)age{
_age=age;
OC 对象类型
(2)OC对象类型(retain)
-(void)setCar:(Car *)car
{
//1.先判断是不是新传进来的对象
If(car!=_car)
{
//2 对旧对象做一次release
[_car release];//若没有旧对象,则没有影响
//3.对新对象做一次retain
_car=[car retain];
}
assign
– (void)setName:(NSString*)name{
_name = name;
}
retain 线程安全下的setter 、getter 方法
-(NSString *)value{
@synchronized(self){
return [[_value retain] autorelease];
}
}
– (void)setValue:(NSString *)aValue{
@synchronized(self){
[aValue retain];
[_value release];
_value = aValue;
}
}
delloc 方法的代码规范
一定要[super dealloc],而且要放到*后
对self(当前)所拥有的的其他对象做一次release操作
-(void)dealloc{
[_car release];
[super dealloc];
}
property的参数 属性的属性
具体请看
http://blog.csdn.net/ci915194561/article/details/50161257
ARC 自动管理内存
ARC 并不是Java 的垃圾回收机制
ARC新增两个关键字:strong 和 weak
strong的含义和retain相同,weak和assign相同,修饰完的属性变量用法也是完全没有改变,不过strong和weak只能修饰对象。
区分强弱指针
强指针:默认的情况下,所有的指针都是强指针,关键字strong
弱指针:_ _weak关键字修饰的指针
声明一个弱指针如下:
_ _weak Person *p;
ARC中,只要弱指针指向的对象不在了,就直接把弱指针做清空操作。
_ _weak Person *p=[[Person alloc] init];//不合理,对象一创建出来就被释放掉,对象释放掉后,ARC把指针自动清零。
ARC中在property处不再使用retain,而是使用strong,在dealloc中不需要再[super dealloc]。
@property(nonatomic,strong)Dog *dog;// 意味着生成的成员变量_dog是一个强指针,相当于以前的retain。
如果换成是弱指针,则换成weak,不需要加_ _。
ARC 中@property的参数:
Strong:相当于原来的retain(适用于OC对象类型),成员变量是强指针
Weak:相当于原来的assign,(适用于oc对象类型),成员变量是弱指针
Assign:适用于非OC对象类型(基础类型)
苹果官方对于ARC机制中对象的内存引用规则:
(1)任何对象,如果仍有持有者,就不会销毁
(2)任何对象,已经没有任何持有者,即自动销毁
1
2
3
持有者就是指向对象的指针,如果是strong修饰的,即是对象的持有者,如果是weak属性的,则不是持有者
ARC机制的使用规则
对于ARC机制的使用,苹果发布了几条重要的规则需要开发者遵守。单单看那些生搬硬套的东西难免生涩,根据开发经验将规则总结如下:
(1)不能调用dealloc,不能重写和调用retain,release,retainCount 和autorelease,同理,@selector(retina),@selector(release)这些曲线救国的方法也不能调用。dealloc虽然能够重写,但是不能调用[super dealloc]之类的方法,CoreFoundation框架由于非从属cocoa框架,所以CFRetain和CFRelease仍然正常使用。
(2)不能使用NSAllocateObjec或NSDeallocateObject函数来创建对象
(3)不能在C语言的结构体中使用对象指针,同时建议用object-c的类来管理数据而不是结构体
(4)不得使用NSAutoreleasePool对象。ARC中,全部使用@autorelease关键字代替,且比NSAutoreleasePool更高效
(5)不得使用内存Zone,那些牵涉NSZone的方法都不得使用。
(6)不得对一个属性变量的取值方法命名以new开头
(7)outlet均用weak关键字修饰,除非他是xib中*顶部的界面元素,则需要strong。
(8)Core Foundation不适合ARC,该创建的仍创建,该释放的仍释放。
关于ARC、MRC下的dealloc 方法使用请看
http://blog.csdn.net/ci915194561/article/details/50159795
Autorelease
可以通过创建和释放内存池控制内存申请和回收的时机.
1、会将对象放到一个自动释放池中
2、当自动释放池被销毁时,会对池子里的所有对象做一次release
3、会返回对象本身
4、调用完autorelease方法后,对象的计数器不受影响(销毁时影响)
优点
1、不需要再关心对象释放的时间
2、不需要再关心什么时候调用release
使用注意
1、占用内存较大的对象,不要随便使用autorelease,应该使用release来精确控制
2、占用内存较小的对象使用autorelease,没有太大的影响
3、内存池是可以嵌套的, 每个内存池都需要有一个创建释放对
NSAutoRealeasePool自动释放池
1、在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。
2、当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中
创建内存 释放池
(1)ios 5.0以前的创建方式
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
““““““““`
[pool release];//[pool drain];用于mac
(2)Ios5.0以后
@autoreleasepool
{//开始代表创建自动释放池
// 即将一个NSString对象加入到*内层的系统内存池, 当我们释放这个内存池时, 其中的对象都会被释放
[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease];
}//结束代表销毁自动释放池
什么情况下需要创建自动释放池?
其实自动释放池存在的意义是为了延迟释放一些对象,延迟向对象发送release消息。在实际的开发中,有两种情况是需要手动创建自动释放池的。
1、在多线程中,因为子线程中可能会使用便利构造器等方法来创建对象,那么这些对象的释放只能放在自动释放池中,此时需要在子线程中添加自动释放池。
注意:主线程已经添加过自动释放池,在main函数里面。
2、就是如果一段代码里面(比如for循环)大量使用便利构造器创建对象,也需要手动添加自动释放池。
使用注意:
1、系统自带的方法中,如果不包含alloc new copy等,则这些方法返回的对象都是autorelease的,如[NSDate date];
2、开发中经常会写一些类方法来快速创建一个autorelease对象,创建对象时不要直接使用类名,而是使用self