------Java培训、Android培训、iOS培训、.Net培训、期待与您交流!
一.自动释放池
1.什么是自动释放池
1>使用场景
我们在程序中通过alloc(new/copy)方法创建了一个对象,就必须在不使用它的时候,对它进行一次release操作。通常情况下,我们可以很明确的知道我们在何时将不再使用它,从而轻松的在某个位置手动对它进行一次release。但是有时候,想知道某个对象在什么时候不再使用并不是那么容易,或者把一些该对象调用方法的代码写到release操作之后,导致程序出错,甚至忘记做release操作,为了解决这类问题,OC引入了自动释放池。
2>自动释放池的工作就是在它销毁的时候对池子里的所有对象进行一次release操作。只要把对象添加到自动释放池,那么对象总会执行一次release操作的。
3>将对象添加到自动释放池实际上只是把对象对release的调用延迟了,对象的引用计数器不会立即改变。
4>自动释放池也是一个对象,就像是一个容器一样。
2.创建自动释放池
1>IOS5.0以前的方式(现淘汰)
程序示例 1:
int main()
{
// 创建一个自动释放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *p = [[[Person alloc] init] autorelease]; //将p对象添加到pool自动释放池中
// 销毁自动释放池
[pool release]; //[pool drain]; 这种方式用在MAC中销毁自动释放池
return 0;
}
2>IOS5.0以后的方式(现使用)
程序示例 2:
int main()
{
// 创建一个自动释放池
@autoreleasepool
{ // "{"代表创建自动释放池
// 凡是在这里调用autorelease方法的对象都会加入该释放池
Person *p = [[[Person alloc] init] autorelease];
} // "}"代表自动释放池的销毁
return 0;
}
3.将对象添加进自动释放池
首先对象要处在自动释放池的范围内,然后调用autorelease方法,它就会被添加到它所处的自动释放池中了。
1>autorelease方法
autorelease方法返回调用对象本身。所以我们通常创建对象的同时还会调用autorelease方法。比如:
Person *p = [[Person alloc] init] autorelease]; 这样就直接创建了一个自动释放的对象。
2>IOS程序运行过程中,会随机创建无数个自动释放池,这些池子都是以栈结构存在(先进后出),只需要向OC对象发送一条autorelease消息,就会把这个对象添加到最近的自动释放池中(栈顶的释放池),实际处在哪个自动释放池,就会放在哪个池子。
3>系统什么时候创建自动释放池子,什么时候销毁自动释放池子是不固定的(一般当用户和屏幕做一些交互的时候)。
4>使用autorelease方法的注意
1)不能连续调用多次autorelease,这意味着当释放池销毁时会对对象做多次release,当对象销毁后,再调用release会发生野指针错误。
2)在创建对象的时候调用了autorelease方法,之后又调用了release。同样会发生野指针错误。
5>autorelease方法的实际应用
1)在实际开发中我们经常会为类添加一些类方法,用于快速创建一个已经autorelease过得对象。(把长长的代码封装到一个方法中)
2)代码规范:
程序示例 3:
// 这类方法的方法名一般以类名开头,或者直接使用类名(首字母小写)
+ (id)person
{
// 这样写子类就不能使用这个方法
//return [[[Person alloc] init] autorelease];
// 用self的话,这个方法还将会满足子类的需求。
return [[[self alloc] init] autorelease];
}
3)一般还会扩充一些方法,对对象进行一些自定义的初始化。
程序示例 4:
+ (id)personWithName:(NSString *)name
{
Person *p = [[[self alloc] init] autorelease];
p.name = @"jack";
return p;
}
4.使用自动释放池好处和坏处
1>好处程序员不用再关心对象内存释放的时间,不用在关心什么时候调用release
1)不能精确的控制对象内存释放的时间,所以要尽量手动进行release。占用内存较大的对象不要随便使用autorelease
2)autorelease适用于占用内存比较小的对象,程序运行时没有太大影响
二.内存管理代码规范
1. 完善setter方法代码
1>基本数据类型:直接复制
2>OC对象类型
程序示例 5:
// _name的setter方法 (_name为OC对象)
- (void)setName:(NSString *)name
{
// 判断是不是新传进来的对象
if (name != _name)
{
// 对旧对象做一次release
[_name release]; // 因为OC没有空指针错误,所以即使_name为空也不会出错
// 对新对象做一次retain
_name = [name retain];
}
}
2. dealloc方法的代码规范
程序示例 6:
// 重写dealloc方法
- (void)dealloc
{
// 在[super dealloc]之前释放相关内存
// 对当前对象(self)所拥有的其他对象做一次release
[_name release]; // 对_name对象做一次release
[super dealloc]; // 必须在最后调用父类的dealloc方法
}
3.使用@property生成setter
1>OC对象类型
添加retain参数,自动生成的setter方法实现中会添加管理内存的代码。
例如:
@property (retain) NSString *name;
2>非OC对象类型
添加assign参数(默认情况下),自动生成的setter方法实现中是对成员变量直接赋值,不会添加管理内存的代码。
例如:
@property (assign) int age;
@property int age;
4.相互引用的问题
如果两个对象都分别持有对方的引用,在内存管理时A对象retain了B对象,B对象retain了A对象,这样会导致A对象和B对象永远无法释放
1>解决方案
1)手动实现时:两个对象的setter方法,其中一个添加内存管理代码,另一个不添加。
2)通过@property时:两个对象一个用retain参数,另一个用assign参数。
2>注意:没有添加内存管理的那一端,在重写dealloc方法实现时,不要对该对象做release操作
5.注意点
1>(非ARC环境)对象类型的成员变量,其setter方法实现中要添加内存管理代码,并且要在dealloc方法实现中对该对象做1次release操作。