iOS内存管理之循环引用

什么是循环引用,我们来看一个例子
定义一个Person类(人),同时也定义一个Card类(身份证类)。每个人都有一个身份证,反之,每个身份证都对应着一个人。这样的引用就叫做“循环引用”。
我们来看代码,定义Person类和Card类。
//Person.h文件
  1.      #import <Foundation/Foundation.h>
  2. #import ”Card.h”
  3. @interface Person : NSObject
  4. @property (nonatomic,retain) Card *card;
  5. @end
//Person.m文件
  1. #import<Foundation/Foundation.h>
  2. #import “Person.h”
  3. @implementation Person
  4. - (void)dealloc
  5. {
  6. NSLog(@“Person类被释放");
  7. [_card release];
  8. [super dealloc];
  9. }
  10. @end
//Card.h文件
  1. #import<Foundation/Foundation.h>
  2. #import “Person.h”
  3. @interface Card : NSObject
  4. @property (nonatomic,retain) Person *person;
  5. @end
//Card.m文件
  1. #import <Foundation/Foundation.h>
  2. #import “Card.h”
  3. @implementation Card
  4. - (void)dealloc
  5. {
  6. [_person release];
  7. NSLog(“Card类被释放");
  8. [super dealloc];
  9. }
  10. @end

//main.m文件
  1. int main()
  2. {
  3. Person *p = [[Person alloc]init];
  4. Card *c = [[Card alloc]init];
  5. p.card = c;
  6. c.person = p;
  7. [c release];
  8. [p release];
  9. return 0;
  10. }

运行后,无结果输出。也就是说发生了内存泄露。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方法,会打印出如下结果:
179439.png
出现内存泄露!


为了解决上述问题,我们做了两点改变。一个是使用了@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将该类的内容包含进来

















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值