什么是循环引用,我们来看一个例子
定义一个Person类(人),同时也定义一个Card类(身份证类)。每个人都有一个身份证,反之,每个身份证都对应着一个人。这样的引用就叫做“循环引用”。
我们来看代码,定义Person类和Card类。
//Person.h文件
- #import <Foundation/Foundation.h>
- #import ”Card.h”
- @interface Person : NSObject
- @property (nonatomic,retain) Card *card;
- @end
//Person.m文件
- #import<Foundation/Foundation.h>
- #import “Person.h”
- @implementation Person
- - (void)dealloc
- {
- NSLog(@“Person类被释放");
- [_card release];
- [super dealloc];
- }
- @end
//Card.h文件
- #import<Foundation/Foundation.h>
- #import “Person.h”
- @interface Card : NSObject
- @property (nonatomic,retain) Person *person;
- @end
//Card.m文件
- #import <Foundation/Foundation.h>
- #import “Card.h”
- @implementation Card
- - (void)dealloc
- {
- [_person release];
- NSLog(“Card类被释放");
- [super dealloc];
- }
- @end
//main.m文件
- int main()
- {
- Person *p = [[Person alloc]init];
- Card *c = [[Card alloc]init];
- p.card = c;
- c.person = p;
- [c release];
- [p release];
- return 0;
- }
运行后,无结果输出。也就是说发生了内存泄露。p和c两个对象均没有被释放,这是为什么呢?我们来看一下retainCount。我们从main文件执行开始
(1)当执行第3行代码时,p的对象计数器为1
(2)当执行第4行代码时,c的对象计数器为1
(3)当执行第5行代码时,c的对象计数器为2
(4)当执行第6行代码时,p的对象计数器为2
(5)当执行第7行代码时,c的对象计数器为1
(6)当执行第8行代码时,p的对象计数器为1
从上面的分析可以看出,程序执行结束后,两个对象计数器均为1,并没有被释放,发生内存泄露。出现内存的泄露的根本原因是对象p和对象c互相retain,导致两个对象均不能被释放。那么如何解决呢?
首先,需要更改Person.h文件和Card.h文件的头文件。两个头文件中,互相引用对方,会导致程序编译报错。在此,我们引入另一个关键字@class,该关键字表示一个类。例如 @class Card; 该句表示Card是一个类,也仅仅表示一个类,不会导入Card.h头文件中的内容。基于此,我们在程序中,将Person.h和Card.h文件修改为如下:
//Person.h文件
#import <Foundation/Foundation.h>
@class Card
//Card.h文件
@import <Foundation/Foundation.h>
@class Person
修改完头文件的@import语句之后,我们继续分析。出现上述的内存泄露的根本原因是两个对象在调用setter方法时,互相retain导致计数器分别加1。对此,我们需要对其中的一各类的setter方法做更改,也就是修改@peroperty关键字。修改的原则是:
(1)A类的setter方法时不要retain(使用assign),dealloc方法中也不进行release操作 (2)B类不变。于是,可以将代码修改如下:
//Person.h文件
#import <Foundation/Foundation.h>
@class Card;
@interface Person : NSObject
@property (nonatomic,
retain) Card *card;
@end
//Person.m文件
#import<Foundation/Foundation.h>
#import “Card.h” //
一定要引用,因为在Person.h文件中只是用了@class做了标记,并没有对其导入,如果不引 //用的话, _care release 会报错,提示没有该方法
@implementation Person
- (void)dealloc()
{
[_card release];
NSLog(@“Person类被销毁");
[super dealloc];
}
@end
//Card.h文件
#import<Foundation/Foundation.h>
@class Person;
@interface Card : NSObject
@porperty (nonatomic,
assgin) Person *person;
@end
//Card.m文件
#import<Foundation/Foundation.h>
#import “Person.h”
@implementation Card
- (void)dealloc
{
NSLog(@“Card类被销毁");
[super dealloc];
}
@end
经过上述修改,再次执行main方法,会打印出如下结果:
未出现内存泄露!
为了解决上述问题,我们做了两点改变。一个是使用了@class代替#import(但是.m文件中仍然要import),另一个是类A使用retain 类B使用assign。我们现在来看一下@class和#import的区别。
@class和#import的区别
1、@class只表示类,#import会将类中的内容导入
2、如果A类被很多类引用,也就是说B类中#imoprt A.h , C类中引用了B.h 或者C类中又引用了A.h。如果A类发生改变,就需要重新编译B C 类,性能降低
3、@class可以用来解决循环引用的问题,如果循环引用使用#import的话,会出现编译错误。
4、一般情况,@class只在.h文件中使用,因为它不会包含该类的内容,所以在使用这个类的时候,也就是在.m文件中,需要使用#import将该类的内容包含进来