,我们才能了解到多个对象之间的内存管理思想。
#import <Foundation/Foundation.h>
#import "RHRoom.h"
NS_ASSUME_NONNULL_BEGIN
@interface RHPerson : NSObject
{
RHRoom *_room;
}
- (void)setRoom:(RHRoom *)room;
- (RHRoom *)room;
@end
NS_ASSUME_NONNULL_END
#import "RHPerson.h"
@implementation RHPerson
- (void)setRoom:(RHRoom *)room {
if (_room != room) {
[_room release];
_room = [room retain];
}
}
- (RHRoom *)room {
return _room;
}
- (void)dealloc {
[_room release];
[super dealloc];
NSLog(@"%s", __func__);
}
@end
三、 @property
参数
- 在成员变量前加上
@property
,系统就会自动帮我们生成基本的 setter
/ getter
方法,但是不会生成内存管理相关的代码。
@property(nonatomic) int val;
- 同样如果在
property
后边加上 assign
,系统也不会帮我们生成 setter
方法内存管理的代码,仅仅只会生成普通的 getter
/ setter
方法,默认什么都不写就是 assign
。
@property(nonatomic, assign) int val;
- 如果在
property
后边加上 retain
,系统就会自动帮我们生成 getter
/ setter
方法内存管理的代码,但是仍需要我们自己重写 dealloc
方法。
@property(nonatomic, retain) RHRoom *room;
四、自动释放池
1、自动释放池
当我们不再使用一个对象的时候应该将其空间释放,但是有时候我们不知道何时应该将其释放。为了解决这个问题,Objective-C 提供了 autorelease
方法。
autorelease
是一种支持引用计数的内存管理方式,只要给对象发送一条 autorelease
消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的「所有对象」做一次 release
操作。
注意:这里只是发送 release
消息,如果当时的引用计数(reference-counted)依然不为 0,则该对象依然不会被释放。
autorelease
方法会返回对象本身,且调用完 autorelease
方法后,对象的计数器不变。
NSObject *obj = [NSObject new];
[obj autorelease];
NSLog(@"obj.retainCount = %zd", obj.retainCount);
2、使用 autorelease 有什么好处呢?
- 不用再关心对象释放的时间
- 不用再关心什么时候调用release
3、autorelease 的原理实质上是什么?
? autorelease
实际上只是把对 release
的调用延迟了,对于每一个 autorelease
,系统只是把该对象放入了当前的 autorelease pool
中,当该 pool
被释放时,该 pool
中的所有对象会被调用 release
方法。
4、autorelease 的创建方法
// 第一种方式:使用 NSAutoreleasePool 创建
// 创建自动释放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// 销毁自动释放池
[pool release];
// 第二种方式:使用 @autoreleasepool 创建
@autoreleasepool {
// 开始代表创建自动释放池
// 结束代表销毁自动释放池
}
5、autorelease 的使用方法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
RHPerson *p = [[[RHPerson alloc] init] autorelease];
[pool release];
@autoreleasepool {
// 开始代表创建自动释放池
RHPerson *p = [[[RHPerson alloc] init] autorelease];
// 结束代表销毁自动释放池
}
6、autorelease 的注意事项
- 并不是放到自动释放池代码中,都会自动加入到自动释放池
@autoreleasepool {
// 因为没有调用 autorelease,所以没有加入到自动释放池中
RHPerson *p = [[RHPerson alloc] init];
// 结束代表销毁自动释放池
}
- 在自动释放池的外部发送 autorelease 不会被加入到自动释放池中
autorelease
是一个方法,只有在自动释放池中调用才有效。
@autoreleasepool {
}
// 没有与之对应的自动释放池, 只有在自动释放池中调用autorelease才会放到释放池
Person *p = [[[Person alloc] init] autorelease];
[p run];
// 正确写法
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
}
// 正确写法
Person *p = [[Person alloc] init];
@autoreleasepool {
[p autorelease];
}
7、自动释放池的嵌套使用
- 自动释放池是以栈的形式存在。
- 由于栈只有一个入口,所以调用
autorelease
会将对象放到栈顶的自动释放池。
栈顶就是离调用 autorelease
方法最近的自动释放池。
@autoreleasepool { // 栈底自动释放池
@autoreleasepool {
@autoreleasepool { // 栈顶自动释放池
Person *p = [[[Person alloc] init] autorelease];
}
Person *p = [[[Person alloc] init] autorelease];
}
}
- 自动释放池中不适宜放占用内存比较大的对象。
- 尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用。
- 不要把大量循环操作放到同一个
@autoreleasepool
之间,这样会造成内存峰值的上升。
// 内存暴涨
@autoreleasepool {
for (int i = 0; i < 99999; ++i) {
Person *p = [[[Person alloc] init] autorelease];
}
}
// 内存不会暴涨
for (int i = 0; i < 99999; ++i) {
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];