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

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

  • 根據(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,如下實(shí)例:

//類型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, 實(shí)例如下:

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

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

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

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

-(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)存管理

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

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

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

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

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

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

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

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

-(void)test
{

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

循環(huán)引用

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

  • __block:在非ARC中使用,NSMallocBlock類型的block不會(huì)對(duì)__block修飾的的變量引用計(jì)數(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(@"測(cè)試對(duì)象 被釋放了。。。");
    
    [super dealloc];
}


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

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


@end

//main.h

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

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

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

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

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

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