Block的內(nèi)存管理,看這里就夠了

最近發(fā)現(xiàn)很多開發(fā)者對block的理解并不是很深,很多項目當中使用的時候多多少會有些問題,今天給大家詳細講講block的內(nèi)存管理, 主要從以下幾個方面來講:

  • 根據(jù)內(nèi)存劃分block的類型
  • block內(nèi)存管理
  • 防止循環(huán)引用

B?lock類型

根據(jù)Block在內(nèi)存中的位置,系統(tǒng)把Block分為3類:NSGlobalBlockNSStackBlock, NSMallocBlock;

  • NSGlobalBlock:位于內(nèi)存全局區(qū)
  • NSStackBlock:位于內(nèi)存棧區(qū)
  • NSMallocBlock:位于內(nèi)存堆區(qū)

我們通過block引用不同的變量來

全局區(qū)block(NSGlobalBlock)

沒有引用局部變量的block叫做NSGlobalBlock,如下實例:

//類型1:沒有使用任何外部變量
-(void)test
{
    void (^gBlock1)(int , int ) =^(int a, int b){
        NSLog(@"a + b = %d", a+b);
    };
    
    NSLog(@"%@", gBlock1);
    //打印結(jié)果為:
    //<__NSGlobalBlock__: 0x1025e8110>
}

//類型2:使用全局變量

//全局變量
int a = 10;

-(void)test
{
    void (^gBlock)() = ^(){
        NSLog(@"%d", a);
    };
    
    NSLog(@"%@", gBlock);
    //輸出結(jié)果為:
    //<__NSGlobalBlock__: 0x103676110>
}


棧區(qū)block(NSStackBlock)

引用了局部變量的block叫做NSStackBlock, 實例如下:

-(void)test
{
    //局部變量
     NSArray *arr = @[@"zhangsan", @"lisi"];
    
    void (^sBlock)() = ^(){
        NSLog(@"arr = %@", arr);
    };
    
    NSLog(@"%@", sBlock);
    //輸出結(jié)果為:
    //<__NSStackBlock__: 0x7fff5bbf1a58>
}

PS:棧區(qū)block在方法返回后就會被釋放,所以只能在方法內(nèi)部使用,如果將他賦值給其他對象或者存儲起來,后面使用時將會出現(xiàn)錯誤.

堆區(qū)Block(***NSMallocBlock ***)

在非ARC下,我們一般不手動創(chuàng)建NSMallocBlock,我們把從棧區(qū)復制(copy)過來的block稱為堆區(qū)block。實例如下:

-(void)test
{

    NSArray *arr = @[@"zhangsan", @"lisi"];
    
    //棧區(qū)block
    void (^sBlock)() = ^(){
        NSLog(@"arr = %@", arr);
    };
    NSLog(@"%@", sBlock);

    //堆區(qū)block
    void (^mBlock)() = [sBlock copy];
    NSLog(@"%@", mBlock);
    
    //輸出結(jié)果為:
    //<__NSStackBlock__: 0x7fff59bf9a38>
    //<__NSMallocBlock__: 0x7fc173f0dd80>
 
}

Block內(nèi)存管理

對block自身內(nèi)存的管理

對于block,有兩個內(nèi)存管理方法:Block_copy, Block_release;Block_copycopy等效, Block_releaserelease等效;

不管是對block進行retian,copy,release,block的引用計數(shù)都不會增加,始終為1;

  • NSGlobalBlock:使用retain,copy, release都無效,block依舊存在全局區(qū),且沒有釋放, 使用copyretian只是返回block的指針;

  • NSStackBlock:使用retain,release操作無效;棧區(qū)block會在方法返回后將block空間回收; 使用copy將棧區(qū)block復制到堆區(qū),可以長久保留block的空間,以供后面的程序使用;

  • NSMallocBlock:支持retian,release,雖然block的引用計數(shù)始終為1,但內(nèi)存中還是會對引用進行管理,使用retain引用+1, release引用-1; 對于NSMallocBlock使用copy之后不會產(chǎn)生新的block,只是增加了一次引用,類似于使用retian;

對引用變量的內(nèi)存管理

在block中經(jīng)常會用到外部變量/對象,如果這個block是存儲在堆區(qū),或者被復制到堆區(qū),則對象對應(yīng)的實例引用+1,當block釋放后block的引用-1;

-(void)test
{

    NSArray *arr = @[@"zhangsan", @"lisi"];
    NSLog(@"arr.retianCount = %ld", arr.retainCount);
    
    //棧區(qū)block
    void (^sBlock)() = ^(){
        NSLog(@"arr = %@", arr);
    };
    //棧區(qū)block不會對引用的變量引用計數(shù)+1
    NSLog(@"arr.retianCount = %ld", arr.retainCount);
    
    
    //堆區(qū)block
    void (^mBlock)() = [sBlock copy];
    //復制到堆區(qū)后,引用計數(shù)+1
    NSLog(@"arr.retianCount = %ld", arr.retainCount);
}

循環(huán)引用

因為block中會對引用的對象進行持有(引用計數(shù)+1),從而導致相互持有引起循環(huán)引用;解決這種問題的方式是對引用變量使用修飾詞__block或者__weak;

  • __block:在非ARC中使用,NSMallocBlock類型的block不會對__block修飾的的變量引用計數(shù)+1,從而消除循環(huán)引用;在ARC中使用__block無效
  • __weak:在ARC中使用,作用和__block一樣,從而消除循環(huán)引用;在非ARC中不可以使用__weak;

防止循環(huán)引用案例:

//TestClass.h

@interface testClass : NSObject

@property (nonatomic, copy)void (^myBlock)(void);

@end

//TestClass.m

#define TestClassExample3 1

@implementation TestClass


-(void)dealloc
{
    NSLog(@"測試對象 被釋放了。。。");
    
    [super dealloc];
}


-(instancetype)init
{
    self = [super init];
    if (self) {
    
#if TestClassExample1
        
        //會引起循環(huán)應(yīng)用,當前對象無法被釋放
        self.myBlock = ^(){
            //增加自己本身的引用計數(shù)
            [self doSomething];
        };
        
#elif TestClassExample2
        
        //在非ARC下有效,防止循環(huán)引用
        //在ARC下無效,會產(chǎn)生循環(huán)引用
        __block TestClass *weakSelf = self;
        self.myBlock = ^(){
            
            //在非ARC下不會增加self的引用計數(shù)
            [weakSelf doSomething];
        };
        
#elif TestClassExample3
        
        //在非ARC下無效,會產(chǎn)生循環(huán)引用
        //在ARC下有效,防止循環(huán)應(yīng)用
        __weak TestClass *weakSelf = self;
        self.myBlock = ^(){
            
            //在非ARC下不會增加self的引用計數(shù)
            [weakSelf doSomething];
        };
        
        
#endif
    }
    
    return self;
}

-(void)doSomething
{
    NSLog(@"測試程序");
}


@end

//main.h

int main(int argc, char * argv[]) {
    @autoreleasepool {
    
        TestClass *tc = [[TestClass alloc] init];
        [tc release];
        tc = nil;
    }
}

歡迎大家踴躍評論,讓我們一起探討技術(shù)??!
如果覺得文章不錯,請幫忙點擊文章下方的喜歡??!
你的支持將是對我最好的鼓勵, 謝謝?。?!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Block簡介(copy一段) Block作為C語言的擴展,并不是高新技術(shù),和其他語言的閉包或lambda表達式是...
    qui丶MyLove閱讀 494評論 0 0
  • 前言 Blocks是C語言的擴充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,851評論 0 23
  • Block類型 根據(jù)Block在內(nèi)存中的位置,系統(tǒng)把Block分為3類:NSGlobalBlock,NSStack...
    成熱了閱讀 605評論 0 0
  • 《Objective-C高級編程》這本書就講了三個東西:自動引用計數(shù)、block、GCD,偏向于從原理上對這些內(nèi)容...
    WeiHing閱讀 10,085評論 10 69
  • 昨天做了一件甜蜜的事。在關(guān)了店門躺在床上時,我和大林提起我手的事情,自然而然說到了瑜伽的事情。我從小沒堅持做一...
    曾曾的麻麻閱讀 260評論 0 0

友情鏈接更多精彩內(nèi)容