您的位置:首页 > 移动开发 > Objective-C

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

2015-09-26 16:39 501 查看
@关联对象的内存管理:

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

创建一个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操作相当于对着空气说话,没有任何反应。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: