Block產生的內存泄露,以及解決方法


前言:

在ARC(自動引用技術)前,Objective-c都是手動來分配釋放 釋放 計數內存,其過程非常復雜。

 ARC技術推出后,貌似世界和平了很多,但是其實ARC並不等同於Java或者C#中的垃圾回收,ARC計數只是在XCode在編譯的時候自動幫我們加上了釋放 計數+1 計數-1.

 

內存泄露例子:

然而在一些特殊的情況下,內存泄露依然存在,而且防不慎防,這里講一下Objective-C中Block計數是如何產生內存泄露的,如下代碼

.h中

typedef void (^CompletionBlock)(NSString *aStr); 
@interface B : NSObject
@property (copy) CompletionBlock completionBlock;
@property (copy) NSString *str; 
@end

  

 .m中

@implementation B
-(id)init{ 
    self = [super init];
    if(self){
        self.str = @"init string value";
    }
    return self;
}

-(void)doAction
{
    __block B *b1 = self;
    self.completionBlock = ^(NSString *aStr){
        b1.str = aStr;
    };
    self.completionBlock(@"new string value");
}

-(void)dealloc{
    NSLog(@"dealloc B");
}
@end

  

 

main函數中

B *b = [[B alloc]init];
[b doAction];
b = nil;//這句有和無其實無所謂

 

 上面的程序看似沒有問題,但是實際上對象b永遠無法釋放,原因在於doAction函數,這個函數里面有一個block函數名為completionBlock ,也就是一個函數指針。這個函數指針在調用的時候有使用一個對象,也就是self對象。但是這個block隱形的做了一件事情——將self引用計數+1了,因此這個時候self對象(也就是main函數中的b對象)的引用計數是2,這個時候即使我執行了b=nil,也無法釋放,因為b=nil只是將計數減1了,而真正釋放的唯一條件是引用計數為0。這就是所謂的Block的循環引用。

如何解決:

  所以在使用block技術的時候,需要格外小心。有幾個解決方法

approach 1: 讓block里面的self的引用計數不要+1,這個時候做法是將"   __block B *b1 = self;"這一行改為,"  __weak __block B *b1 = self;",表示說“我block里面雖然會用到self,但是別擔心,我不會講引用計數+1的”

approach 2:在doAction函數內存的最后一行添加 self.completionBlock=nil;  因為block內部將self計數+1了,但是如果這個block自己先消亡,那么與之相關的一切都講消亡(當然對於引用計數大於1的對象,不會消亡,只會計數減1)。

附加:

PS:開發中,幾乎每個.m文件都會用到block技術,但是從未發現和在意這個內存泄露問題,這並不是XCode編譯時的優化,而是我們所用到的Block技術(例如AFNetwork GCD Animation)中的block都是匿名Block——即,用完自動釋放。 如果有一天不用匿名block就需要注意這個問題了。

例如下面的例子中,雖然使用了Block,但是沒有泄露,是因為這是一個匿名的Block(即匿名函數指針)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
           self.view.backgroundColor = [UIColor redColor];
    });

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM