Retain特质属性在MRC中的陷阱以及ARC中对其的规避

Retain特质属性在MRC中的陷阱以及ARC中对其的规避

 

现在的xcode建立的工程中默认使用arc,要想在你的程序中使用MRC需要在build phases->Compile Sources中找到你的文件在Complie Flags中加入-fno-objc-arc。

Retain属性的定义如下

@property(nonatomic,retain)NSString* retainPropertyString;

先看一段正常的程序代码

- (void)viewDidLoad {

   [super viewDidLoad];

NSString*  p1 =[[NSString alloc] initWithFormat:@"123"]; //p1 refcount = 1

self. retainPropertyString = p1; //p1 refcount = 2

[p1 release]; //release p1, refcount = 1

}

-(void)viewDidUnload{

                      self. retainPropertyString= nil;

/*[[NSString alloc] initWithFormat:@"123"] 这段内存块至此会被销毁*/

}

以上代码是一个正常的逻辑没有内存泄露也没有非法内存的存在。其中隐藏细节便是retainPropertyString属性设置方法的实现,编译器的默认实现如下

-(void)setRetainPropertyString:(NSString*)propertyString

{

                      [propertyString retain];

                      [_retainPropertyStringrelease];

                      _retainPropertyString =propertyString;

}

文字描述即使先保留新值,后释放旧值,最后更新实例变量令其指向新值。顺序很重要。加入还未保留新值就先把就只release,而两个值又指向同一块内存,并且那块内存在外没有被其他对象强引用,就会导致系统将那块内存永久释放。而再进行保留也无法令已经回收的内存复活。Strong强引用属性默认设置过程如同retain。

现在如果将以上viewDidLoad中代码改成如下

- (void)viewDidLoad {

   [super viewDidLoad];

self. retainPropertyString = [[NSString alloc] initWithFormat:@"123"];

}

那么retainPropertyString所指向的内存块的引用计数为2,即使在viewDidUnload中设置self.retainPropertyString = nil,也无法避免存放“123”这块内存泄露了。并且在程序其他地方更有可能写出self.retainPropertyString = [[NSStringalloc] initWithFormat:@"abc"];

这样导致多处内存泄露。

如上所述,在mrc中如何规避这种内存泄露问题

1.    使用临时对象p1指向[[NSString alloc] initWithFormat:@"123"],在不需要的时候release p1. 就像一开始阐述的那样

2.    self.retainPropertyString = [[NSString alloc] initWithFormat:@"123"];改写成

_ retainPropertyString =  [[NSString alloc]initWithFormat:@"123"];

3.    self.retainPropertyString = [NSSting stringWithFormat:@”123”];也可,这种情况自动释放池会避免内存泄露

4.    在dealloc方法中调用[retainPropertyString release];

 

以下我们来看一下ARC对上述出现的问题是如何做到规避的

我们创建两个NSObject的子对象来观察内存执行情况

// ViewController.m

#import"ViewController.h"

#import "TestObj.h"                              

@interfaceViewController ()

@end

@implementationViewController

- (void)viewDidLoad{

    [super viewDidLoad];

// Do any additional setup after loading the view,typically from a nib.

NSLog(@"begin test...");

   TestObj* p = [[TestObj alloc] init];

}

@property(strong,nonatomic)TestObj1* mObj1;

//TestObj.m

#import "TestObj.h"

#import "TestObj1.h"

@implementation TestObj

-(id)init

{

    if([super init]) {

       self.mObj1 = [[TestObj1 alloc] init];

}

return self;

}

-(void)dealloc{   

}

@end

 

// TestObj1.m

#import "TestObj1.h"

@implementation TestObj1

-(void)dealloc{

   NSLog(@"mObj1 dealloc");

}

@end

 

以上代码输出结果如下:

2015-11-13 14:02:54.609 TestInvoked[50965:4982790] begintest...

2015-11-13 14:02:59.234 TestInvoked[50965:4982790] mObjdealloc

2015-11-13 14:02:59.235TestInvoked[50965:4982790] mObj1 dealloc

 

在TestObj.m文件中使用了self.mObj1 = [[TestObj1 alloc] init];这种方式在以前MRC中会导致内存泄露,在此我们发现TestObj1对象正常释放了。

 

再进一步看将TestObj.m中的init方法中写成

self.mObj1 = [[TestObj1 alloc] init];       

self.mObj1 =[[TestObj1 alloc] init];

按照MRC中得情况会出现两次内存泄露,以下看ARC中得执行情况

2015-11-13 14:10:04.647TestInvoked[51397:4987676] begin test...

2015-11-13 14:10:09.000TestInvoked[51397:4987676] mObj1 dealloc

2015-11-13 14:10:09.000TestInvoked[51397:4987676] mObj dealloc

2015-11-13 14:10:09.000TestInvoked[51397:4987676] mObj1 dealloc

我们发现ARC帅呆了,TestObj1对象被释放两次,没有内存泄露的情况。

 

为什么TestObj和TestObj1对象都能正常释放,我们来一步步的分析一下

先看测试开始处

TestObj* p = [[TestObjalloc] init];

执行完这段代码,紧接着就是} ,编译器会为我们隐性的插入[p release];这样的代码,好这样TestObj对象自然不会内存泄露。

接下来我们在看TestObj.m中的init方法中

self.mObj1 = [[TestObj1 alloc] init];        //MRC中此处必然有泄漏

self.mObj1 =[[TestObj1 alloc] init];       //MRC中此处必然有泄漏

编译器处理过后的代码应该是如下这个样子

self.mObj1 = [[TestObj1 alloc] init];

[_mObj1 release];

self.mObj1 =[[TestObj1 alloc] init]; //此时之前分配的TestObj1对象就会被释放,此时mObj1指向了另外分配的内存

[_mObj1 release]

 

 

以上如有不实之处望各位同行指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值