MRC-block與ARC-block

上一篇已經講解了MRC與ARC的基本知識,本篇咱們講解MRC-block與ARC-block的基本內容。多線程

 

在MRC時代,Block會隱式地對進入其做用域內的對象(或者說被Block捕獲的指針指向的對象)加retain,來確保Block使用到該對象時,可以正確的訪問。線程

這件事情在下面代碼展現的狀況中要更加額外當心。指針

MyViewController *myController = [[MyViewController alloc] init…];對象

 

// 隱式地調用[myController retain];形成循環引用內存

myController.completionHandler =  ^(NSInteger result) {作用域

   [myController dismissViewControllerAnimated:YES completion:nil];編譯器

};it

[self presentViewController:myController animated:YES completion:^{內存管理

   [myController release]; // 注意,這裏調用[myController release];是在MRC中的一個常規寫法,並不能解決上面循環引用的問題io

}];

 

在這段代碼中,myController的completionHandler調用了myController的方法[dismissViewController...],這時completionHandler會對myController作retain操做。而咱們知道,myController對completionHandler也至少有一個retain(通常準確講是copy),這時就出現了在內存管理中最糟糕的狀況:循環引用!簡單點說就是:myController retain了completionHandler,而completionHandler也retain了myController。循環引用致使了myController和completionHandler最終都不能被釋放。咱們在delegate關係中,對delegate指針用weak就是爲了不這種問題。

不過好在,編譯器會及時地給咱們一個警告,提醒咱們可能會發生這類型的問題:

對這種狀況,咱們通常用以下方法解決:給要進入Block的指針加一個__block修飾符。

這個__block在MRC時代有兩個做用:

•說明變量可改

•說明指針指向的對象不作這個隱式的retain操做

一個變量若是不加__block,是不能在Block裏面修改的,不過這裏有一個例外:static的變量和全局變量不須要加__block就能夠在Block中修改。

MyViewController * __block myController = [[MyViewController alloc] init…];

// ...

myController.completionHandler =  ^(NSInteger result) {

    [myController dismissViewControllerAnimated:YES completion:nil];

};

//以後正常的release或者retain

 

在ARC引入後,沒有了retain和release等操做,狀況也發生了改變:在任何狀況下,__block修飾符的做用只有上面的第一條:說明變量可改。即便加上了__block修飾符,一個被block捕獲的強引用也依然是一個強引用。這樣在ARC下,若是咱們還按照MRC下的寫法,completionHandler對myController有一個強引用,而myController對completionHandler有一個強引用,這依然是循環引用,沒有解決問題:(

 

因而咱們還須要對原代碼作修改。簡單的狀況咱們能夠這樣寫:

__block MyViewController * myController = [[MyViewController alloc] init…];

// ...

myController.completionHandler =  ^(NSInteger result) {

    [myController dismissViewControllerAnimated:YES completion:nil];

    myController = nil;  // 注意這裏,保證了block結束myController強引用的解除

};

在completionHandler以後將myController指針置nil,保證了completionHandler對myController強引用的解除,不過也同時解除了myController對myController對象的強引用。這種方法過於簡單粗暴了,在大多數狀況下,咱們有更好的方法。

 

這個更好的方法就是使用weak。(或者爲了考慮iOS4的兼容性用unsafe_unretained,具體用法和weak相同,考慮到如今iOS4設備可能已經絕跡了,這裏就不講這個方法了)(關於這個方法的本質咱們後面會談到)

 

爲了保證completionHandler這個Block對myController沒有強引用,咱們能夠定義一個臨時的弱引用weakMyViewController來指向原myController的對象,並把這個弱引用傳入到Block內,這樣就保證了Block對myController持有的是一個弱引用,而不是一個強引用。如此,咱們繼續修改代碼:

MyViewController *myController = [[MyViewController alloc] init…];

// ...

MyViewController * __weak weakMyViewController = myController;

myController.completionHandler =  ^(NSInteger result) {

    [weakMyViewController dismissViewControllerAnimated:YES completion:nil];

};

這樣循環引用的問題就解決了,可是卻不幸地引入了一個新的問題:因爲傳入completionHandler的是一個弱引用,那麼當myController指向的對象在completionHandler被調用前釋放,那麼completionHandler就不能正常的運做了。在通常的單線程環境中,這種問題出現的可能性不大,可是到了多線程環境,就很很差說了,因此咱們須要繼續完善這個方法。

爲了保證在Block內可以訪問到正確的myController,咱們在block內新定義一個強引用strongMyController來指向weakMyController指向的對象,這樣多了一個強引用,就能保證這個myController對象不會在completionHandler被調用前釋放掉了。因而,咱們對代碼再次作出修改:

 

MyViewController *myController = [[MyViewController alloc] init…];

// ...

MyViewController * __weak weakMyController = myController;

myController.completionHandler =  ^(NSInteger result) {

    MyViewController *strongMyController = weakMyController;

 

  if (strongMyController) {

        // ...

        [strongMyController dismissViewControllerAnimated:YES completion:nil];

        // ...

    }

    else {

        // Probably nothing...

    }

};

 

到此,一個完善的解決方案就完成了:)

 

以上就是block在ARC與MRC的用處,但願對你們有所幫助!!!

相關文章
相關標籤/搜索