Sian 发表于 2014-3-19 16:27:49

相互引用与相互retain

假设场景与说明:
1、Person对象有个成员变量为Card对象,Card对象中有个成员变量为Person对象;
2、Person对象将Card对象做为其成员变量,需要引入头文件"Card.h",同样Card对象将Person对象做为其成员变量,需要引入头文件"Person.h",那么两个头文件相互引用,会千万死循环,OC中通过使用#import替代C语言中的#include解决了多次引用的问题,但解决不了循环引用的问题,所以在这种情况下,通过@class Person或者@class Card来解决声明问题,至少要有一方使用该方法声明类,才能解决循环引用的问题;
3、另外一个问题:相互retain,要常规模式下,一个对象做为另一个对象的成员变量,示意图如下所示:



4、Card对象做为Person对象的成员变量,创建并赋值给_card后,当前有两个拥有者,所以retainCount = 2,进行操作后,只有_card对其拥有所有权,如果Person对象执行release操作,同时会在dealloc方法中对_card进行release操作,这样对象成功释放,符合黄金法则,一切正常。
5、但如果Card对象中同时也有一个_person对象对Person对象c也有拥有权,这样一来两个对象都无法被释放,先看示意图,再说明不能释放的关键点:



6、如果以Person为主,Card * c做为其成员对象,原则上创建好对象赋值给_card后,将临时变量c进行释放,但c同时又是另一个对象的主对象,因而Card * c在进行alloc操作分配存储空间后只会做一次release操作,不可能做两次release,可事实上,c在赋值给Person * p的成员变量_card时进行了一次retain操作,此时retainCount = 2;
7、如果以Card为主,面临同样的问题,所在关键点在于,两个对象的retainCount值由于相互赋值时都增加了1,进行一次release后,不会调用dealloc方法,所以第二次release无法触发,从而两对象处于互等状态;
8、解决这个问题的办法:将其中一个对象在声明时@property (nonatomic, retain) 时,将retain改成assign,只修改其中一个即可,问题得以解决;

参考代码:
Person.h
//
//Person.h
//Retain-1
//
//Created by yusian on 14-3-19.
//Copyright (c) 2014年 小龙虾论坛. All rights reserved.
//

#import <Foundation/Foundation.h>

@class Card;

@interface Person : NSObject

@property (nonatomic, retain) Card *card;

@end



Person.m
//
//Person.m
//Retain-1
//
//Created by yusian on 14-3-19.
//Copyright (c) 2014年 小龙虾论坛. All rights reserved.
//

#import "Person.h"

@implementation Person

- (void)dealloc
{
    NSLog(@"Person is dealloc...");
   
    ;
   
    ;
}

@end

Card.h
//
//Card.h
//Retain-1
//
//Created by yusian on 14-3-19.
//Copyright (c) 2014年 小龙虾论坛. All rights reserved.
//

#import <Foundation/Foundation.h>

@class Person;

@interface Card : NSObject

@property (nonatomic, assign) Person * person;

@end

Card.m
//
//Card.m
//Retain-1
//
//Created by yusian on 14-3-19.
//Copyright (c) 2014年 小龙虾论坛. All rights reserved.
//

#import "Card.h"

@implementation Card

- (void)dealloc
{
    NSLog(@"Card is dealloc...");
   
    //;
   
    ;
}

@end

main.m
//
//main.m
//Retain-1
//
//Created by yusian on 14-3-19.
//Copyright (c) 2014年 小龙虾论坛. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Card.h"

int main()
{
    Person * p = [ init];
   
    Card * c = [ init];
   
    p.card = c;
   
    c.person = p;
   
    ;
    ;

    return 0;
}
运行结果:
2014-03-19 16:27:33.036 Retain-1 Person is dealloc...2014-03-19 16:27:33.037 Retain-1 Card is dealloc...Program ended with exit code: 0

Sian 发表于 2014-3-20 00:06:46

补充一句,声明文件(.h)中只声明了类@class Person,在实现文件(.m)中需要引用类的声明文件(.h),即在.m文件中#import Person.h
页: [1]
查看完整版本: 相互引用与相互retain