iOS经典讲解之Objective-C关联对象的内存管理


@关联对象的内存管理:

如果两个或者两个以上的对象存在关联,那么在内存管理方面要比一个对象的内存管理复杂一些,下面通过一个简单的例子来讲解一下关联对象的内存管理问题:

创建一个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操作相当于对着空气说话,没有任何反应。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值