家在使用房间
p.room = r; // [p setRoom:r]
[r release]; // 释放房间 r
之后的内存表现为:
接着执行换房操作而不进行其他操作的话,
// 3. 换房
Room *r2 = [[Room alloc] init];
r2.no = 444;
p.room = r2;
内存的表现为:
最后执行完
[r2 release]; // 释放房间 r2
[p release]; // 释放玩家 p
内存的表现为:
可以看出房间 r 并没有被释放,这是因为在进行换房的时候,并没有对房间 r 进行释放。所以应在调用setter方法的时候,对之前的变量进行一次release操作。具体setter方法代码如下:
- (void)setRoom:(Room *)room // room = r
{
// 将以前的房间释放掉 -1
[_room release];
// 对房间的引用计数器+1
[room retain];
_room = room;
}
}
这样在执行完p.room = r2;
之后就会将 房间 r 释放掉,最终内存表现为:
4. 一个玩家使用一个游戏房间,不再使用游戏房间,将游戏房间释放掉之后,再次使用该游戏房间的情况
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1.创建两个对象
Person *p = [[Person alloc] init];
Room *r = [[Room alloc] init];
r.no = 888;
// 2.将房间赋值给人
p.room = r; // [p setRoom:r]
[r release]; // 释放房间 r
// 3.再次使用房间 r
p.room = r;
[r release]; // 释放房间 r
[p release]; // 释放玩家 p
}
return 0;
}
执行下面代码
// 1.创建两个对象
Person *p = [[Person alloc] init];
Room *r = [[Room alloc] init];
r.no = 888;
// 2.将房间赋值给人
p.room = r; // [p setRoom:r]
[r release]; // 释放房间 r
之后的内存表现为:
然后再执行p.room = r;
,因为setter方法会将之前的Room实例对象先release掉,此时内存表现为:
此时_room、r 已经变成了一个野指针。之后再对野指针 r 发出retain消息,程序就会崩溃。所以我们在进行setter方法的时候,要先判断一下是否是重复赋值,如果是同一个实例对象,就不需要重复进行release和retain。换句话说,如果我们使用的还是之前的房间,那换房的时候就不需要对这个房间再进行release和retain。则setter方法具体代码如下:
- (void)setRoom:(Room *)room // room = r
{
// 只有房间不同才需用release和retain
if (_room != room) { // 0ffe1 != 0ffe1
// 将以前的房间释放掉 -1
[_room release];
// 对房间的引用计数器+1
[room retain];
_room = room;
}
}
因为retain不仅仅会对引用计数器+1, 而且还会返回当前对象,所以上述代码可最终简化成:
- (void)setRoom:(Room *)room // room = r
{
// 只有房间不同才需用release和retain
if (_room != room) { // 0ffe1 != 0ffe1
// 将以前的房间释放掉 -1
[_room release];
_room = [room retain];
}
}
以上就是setter方法的最终形式。
6. @property参数
- 在成员变量前加上@property,系统就会自动帮我们生成基本的setter/getter方法
@property (nonatomic) int val;
- 如果在property后边加上retain,系统就会自动帮我们生成getter/setter方法内存管理的代码,但是仍需要我们自己重写dealloc方法
@property(nonatomic, retain) Room *room;
- 如果在property后边加上assign,系统就不会帮我们生成set方法内存管理的代码,仅仅只会生成普通的getter/setter方法,默认什么都不写就是assign
@property(nonatomic, assign) int val;
7. 自动释放池
当我们不再使用一个对象的时候应该将其空间释放,但是有时候我们不知道何时应该将其释放。为了解决这个问题,Objective-C提供了autorelease方法。
- autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的
所有对象做一次release操作
注意,这里只是发送release消息,如果当时的引用计数(reference-counted)依然不为0,则该对象依然不会被释放。
- autorelease方法会返回对象本身,且调用完autorelease方法后,对象的计数器不变
Person *p = [Person new];
p = [p autorelease];
NSLog(@"count = %lu", [p retainCount]); // 计数还为1
1. 使用autorelease有什么好处呢
- 不用再关心对象释放的时间
- 不用再关心什么时候调用release
2. autorelease的原理实质上是什么?
autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用release。
3. autorelease的创建方法
- 使用NSAutoreleasePool来创建
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // 创建自动释放池
[pool release]; // [pool drain]; 销毁自动释放池
- 使用@autoreleasepool创建
@autoreleasepool
{ //开始代表创建自动释放池
} //结束代表销毁自动释放池
4. autorelease的使用方法
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
Person *p = [[[Person alloc] init] autorelease];
[autoreleaseP