内存管理
为什么要进行内存管理?
1>硬件条件的限制:移动设备的内存有限,所以每个App所占的内存也是有限制的,当App占用较多内存时,系统会发出警告,严重的时候引起“闪退”。
2>本质原因在于:存储空间不同,局部变量一般存储在栈中,对象一般存储在堆中,当栈中变量销毁后,对象内存已经无指向,就会内存泄露。
引用计数:
判断对象要不要回收的唯一标准,当计数器为0时,回收对象。
创建对象时,将它的引用次数设置为1,每一次必须保持该对象时,就发送一条retain 消息,使其引用次数加1。
[myFractionretain];
不再需要对象时,可以通过发送release 消息,使对象的引用次数减1。
[myFractionrelease];
想要获取对象当前的计数器值,可以通过发送retainCount消息
[myFractionretainCount];
每个OC对象内部都专门有4个字节的存储空间来存储引用计数器。
对象的销毁:
当对象被销毁时,系统会自动想对象发送一条delloc消息,一般会重写delloc方法,在这里释放相关的资源。一旦重写了delloc方法就必须在最后调用[super delloc]。
一旦对象被回收了,那么他所占据的存储空间就不可用,指向对象的指针最好赋值为空,否则会引起野指针错误,后果不可预知。
野指针: 一个指向已经不存在的内存的指针。
僵尸对象:已经被回收的对象,或者说对象所对应的内存地址已经不可用的对象称为僵尸对象。过度释放会导致僵尸对象的产生。
空指针:没有指向任何东西的指针,给空指针发送消息不会报错。
内存管理的原则:
1>原则
只要还有人在使用某个对象,那么这个对象就不会被回收;
只要你想使用这个对象,那么久应该让这个对象的引用计数+1;
当你不想使用这个对象时,应该让对象的引用计数-1。
2>谁创建,谁release
3>谁retain,谁release
只要你调用了retain,无论这个对象如何生成的,你都要掉调用release。
4>有始有终,有加就应该有减。曾经让某个对象计数器+1,就应该让其在最后-1。
内存管理代码规范
1>只要调用了alloc,就必须有release(autorelease)
2>Set方法的代码规范
1. 基本类型
- (void) setAge : (int)age
{
_age = age;
}
2. OC对象类型
- (void) setCar : (Car*)car
{
// 1.先判断是不是新传进来的对象
if( car != _car)
{
// 2.对就对象进行一次release
[_car release]; //若没有就对象,则没有影响
// 3.对新对象进行一次retain
_car = [_car retain];
}
}
3>重写delloc方法的代码规范
1. 一定要在最后调用[super delloc]
2. 对self(当前)所拥有的对象做一release操作
- (void) delloc
{
[_car release];
[super release];
}
@property的参数:
1> 内存管理相关参数
Retain: 对对象release旧值,retain新值(适用于OC对象类型)
Assign: 直接赋值(默认,适用于非oc对象类型)
Copy: release旧值,copy新值
2> 是否要生成set方法(若为只读属性,则不生成)
Readonly: 只读,只会生成getter的声明和实现
Readwrite: 默认的,同时生成setter和getter的声明和实现
3> 多线程管理(苹果在一定程度上屏蔽了多线程操作)
Nonatomic: 高性能,一般使用这个
Atomic: 低性能
4> set和get方法的名称
修改set和get方法的名称,主要用于布尔类型。因为返回布尔类型的方法名一般以is开头,修改名称一般用在布尔类型中的getter。
@property(setter=setAbc,getter=isRich) BOOL rich;
BOOL b = p.isRich;// 调用
内存管理中的循环引用问题以及解决:
案例:每个人有一张身份证,每张身份证对应一个人,不能使用#import的方式相互包含,这就形成了循环引用。
@class 类名——解决循环引用问题,提高性能
@class仅仅告诉编译器,在进行编译的时候把后面的名字作为一个类来处理。
1> @class的作用:声明一个类,告诉编译器某个名称是一个类
2> 开发中引用一个类的规范
1. 在.h文件中使用@class来声明类
2. 在.m文件中真正要使用到的时候,使用#import来包含类中的所有东西
3> 两端循环引用的解决方法
一端使用retain,一端使用assign(使用assign的在dealloc中也不用再release)
Autorelease:
1> 基本用法
1. 会将对象放到一个自动释放池中
2. 当自动释放池被销毁时,会对池子里的所有对象做一次release
3. 会返回对象本身
4. 调用完autorelease方法后,对象的计数器不受影响(销毁时影响)
2> 好处
1. 不需要再关心对象释放的时间
2. 不需要再关心什么时候调用release
3> 使用注意
1. 占用内存较大的对象,不要随便使用autorelease,应该使用release来精确控制
2. 占用内存较小的对象使用autorelease,没有太大的影响
4> 错误写法
1. 连续调用多次autorelease,释放池销毁时执行两次release
2. Alloc之后调用了autorelease,之后又调用了release。
5> 自动释放池
1. 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。
2. 当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中。
6> 自动释放池的创建方式
1. iOS5.0以前的创建方式
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
`````````````````
[pool release];//[pool drain];用于mac
2. iOS5.0以后
@autoreleasepool
{//开始代表创建自动释放池
·······
}//结束代表销毁自动释放池
7> Autorelease注意
1. 系统自带的方法中,如果不包含alloc new copy等,则这些方法返回的对象都是autorelease的,如[NSDate date];
2. 开发中经常会写一些类方法来快速创建一个autorelease对象,创建对象时不要直接使用类名,而是使用self
ARC内存管理机制:
1> ARC的判断准则:
只要没有强指针指向对象,对象就会被释放。
2> 指针分类:
1. 强指针:默认的情况下,所有的指针都是强指针,关键字strong
2. 弱指针:_ _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,不需要加_ _。
3> ARC的特点总结:
1. 不允许调用release,retain,retainCount
2. 不允许重写dealloc,但是不允许调用[super dealloc]
3. @property的参数:
Strong:相当于原来的retain(适用于OC对象类型),成员变量是强指针
Weak:相当于原来的assign,(适用于oc对象类型),成员变量是弱指针
Assign:适用于非OC对象类型(基础类型)
4> 补充
让程序兼容ARC和非ARC部分。转变为非ARC -fno-objc-arc 转变为ARC 的, -f-objc-arc 。
ARC也需要考虑循环引用问题:一端使用retain,另一端使用assign。
提示:字符串是特殊的对象,但不需要使用release手动释放,这种字符串对象默认就是autorelease的,不用额外的去管内存。
内存管理规则摘要:
1> 释放对象,可以释放其所占的内存,规则是:不再使用创建或者保持的对象时,就释放它们。
2> 发送一条release 消息不一定销毁对象,只有引用计数变为0 时,才销毁这个对象。系统向对象发送一条dealloc 消息来释放它所占的内存。
3> 自动释放池在释放池本身的时候自动释放池中的对象。系统向每个对象发送一条release 消息,对引用计数变为0 的对象发送一条dealloc 消息来释放它所占的内存。
4> 若你的方法中不再需要一个对象时,但需要返回它,可发送一条autorelease 消息来加入自获得途径临时对象拥有对象alloc/new/copy 不再使用时释放对象在dealloc 方法中释放对象任何其他方法不需要执行任何操作获得对象时保留,在dealloc 方法中释放对象动释放池。
5> 若使用alloc 或copy 方法(或使用allocWithZone:、copyWithZone:或mutableCopy 方法来直接创建对象,则由你负责释放它。每次retain 对象时,应该release 或autoreleas 它。
6> 除了上以规则中提到的方法之外,不必费心地释放其他方法返回的对象,这不是你的责任。