@关联对象的内存管理:
如果两个或者两个以上的对象存在关联,那么在内存管理方面要比一个对象的内存管理复杂一些,下面通过一个简单的例子来讲解一下关联对象的内存管理问题:
创建一个Pet(宠物)类:
#import <Foundation/Foundation.h>
@interface Pet : NSObject
{
// 无实例变量</span>
}
// 声明一个行为方法</span>
- (void)play;
@end
#import "Pet.h"
@implementation Pet
// 简单实现
-(void)play
{
NSLog(@"play......");
}
// 重写dealloc方法,判断对象是否释放
-(void)dealloc
{
NSLog(@"宠物已经被释放");
[super dealloc];
}
@end
然后创建一个Person类,此类的对象可以拥有宠物,从而建立两个对象的关联关系:
#import <Foundation/Foundation.h>
#import "Pet.h"
@interface Person : NSObject
{
// Person对象拥有宠物
Pet *_pet;
}
// 声明一个方法用来测试
- (void)test;
@end
#import "Person.h"
@implementation Person
// 重写初始化方法
-(instancetype)init
{
self = [super init];
if (self) {
// 在初始化对象时创建一只宠物
// 思考:此时的Pet对象在哪释放比较得当
_pet = [[Pet alloc] init];
}
return self;
}
// 实现测试方法
-(void)test
{
// 在宠物对象已经创建出来的情况下,执行方法
if (_pet) {
[_pet play];
}
}
// 重写初始化方法 检测Person对象是否被释放
-(void)dealloc
{
NSLog(@"已经释放");
[super dealloc];
}
@end
然后创建对象进行测试测试:
// 创建一个Person对象 同时也创建了一个Pet对象
Person *person = [[Person alloc] init];
// 执行测试方法
[person test];
// 根据内存管理黄金原则 释放对象
[person release];
此时你运行会发现Person对象已经释放但是Pet对象没有被释放,存在内存问题(内存泄露)
怎么解决哪?如果Person对象释放之后,Pet对象就没有存在的价值了,所以说,在Person对象释放的时候,就应该将Pet对象释放掉,没错,Person对象释放会调用dealloc方法,此时在dealloc方法中将Pet对象释放是最恰当的。
所以在Person类的dealloc方法中添加一句即可,如下:
-(void)dealloc
{
// 添加
[_pet release];
NSLog(@"已经释放");
[super dealloc];
}
情况二 :上面是通过初始化方法直接给Person对象实例变量赋值,如果通过其他方法赋值时比如set方法对其赋值时的内存怎么处理哪,下面通过一个例子来了解一下:
#import <Foundation/Foundation.h>
#import "Pet.h"
@interface Person : NSObject
{
Pet *_pet;
}
// 声明set方法
- (void)setPet:(Pet *)pet;
- (void)test;
@end
#import "Person.h"
@implementation Person
// 实现set方法
-(void)setPet:(Pet *)pet
{
// 思考:此时的写法是否存在内存问题?
_pet = pet;
}
-(void)test
{
if (_pet) {
[_pet play];
}
}
-(void)dealloc
{
// 当Person对象释放时,将其拥有的宠物释放掉
[_pet release];
[super dealloc];
}
@end
通过测试来解决上面思考的问题:
// 创建Person对象
Person *man = [[Person alloc] init];
// 创建Pet对象
Pet *pet = [[Pet alloc] init];
// 通过set方法给Person对象的实例变量赋值
[man setPet:pet];
[man test];
// 根据内存释放原则 释放掉上面alloc出来的对象
[man release];
[pet release];
运行你会发现程序崩溃,原因是什么呢?原因是在Person对象释放时,会释放掉Pet对象,而Pet对象在外面已经释放过一次,就会出现过度释放问题,导致程序崩溃。
解决方法1、(不合理方法)
// 为了不出现重复释放问题,可以在赋值的时候将对象的引用计数加一
// 比如
-(void)setPet:(Pet *)pet
{ _pet = [pet retain];
}
你会发现在测试时,也会出现问题,比如通过set方法对其重新赋值时:
// 创建Person对象
Person *man = [[Person alloc] init];
// 创建Pet对象
Pet *pet = [[Pet alloc] init];
// 通过set方法给Person对象的实例变量赋值
[man setPet:pet];
// 创建第二个Pet对象
Pet *pet2 = [[Pet alloc] init];
// 对Person对象实例变量重新赋值
[man setPet:pet2];
[man test];
// 根据内存释放原则 释放掉上面alloc出来的对象
[man release];
[pet release];
[pet2 release];
此时会出现内存泄露问题,因为retain过两次,而dealloc方法中只释放一次,所以上面解决方法不合理。
解决方法2、(不合理方法)
为了不出现上面内存泄露问题,可以在赋值之前先将原来的释放掉:如
-(void)setPet:(Pet *)pet
{
[_pet release];
_pet = [pet retain];
}
同样会出现问题:
当我们对其赋同样的值时会出现重复操作问题:
如:
// 创建Person对象
Person *man = [[Person alloc] init];
// 创建Pet对象
Pet *pet = [[Pet alloc] init];
// 通过set方法给Person对象的实例变量赋值
[man setPet:pet];
// 对Person对象实例变量再次赋同样的值
[man setPet:pet];
[man test];
// 根据内存释放原则 释放掉上面alloc出来的对象
[man release];
[pet release];
此时没有必要再release和retain,需要对上面的操作再完善一下:
解决方法3、(合理方法)
-(void)setPet:(Pet *)pet
{
if (_pet != pet) {
[_pet release];
_pet = [pet retain];
}
}
这是最完善的操作方法,类似于属性的内存管理。
注意:在实例变量未赋值之前释放是没有问题的,对nil,做release操作相当于对着空气说话,没有任何反应。