iOS開發中Block深入使用探索

來源:互聯網
上載者:User

Block在ios 4.0之後加入,並大量使用在新的ios api中。block是一個匿名的代碼塊,可以作為傳遞給其他對象的參數,並得到傳回值。從本質上講,block同其他普通的變數類似,只是其儲存的資料是一個函數體。Block不只是針對Objective-C的專利,而是一種可以應用於C、C++和OBjective-C的語言層面的新特性。通過使用block,開發人員可以將一段程式碼片段像某一個數值一樣當做參數傳遞給函數。同時,blocks也是Objective-C的一種對象,可以像其他對象一樣添加到NSArray或者NSDictionary等集合中。

塊文法,本質上是匿名函數。與函數指標很相似
Block封裝了一段代碼,可以在任何時候執行
Block可以作為函數參數或者函數的傳回值,而其本身又可以帶輸入參數或傳回值。

Block基本使用

1> 如何定義block變數

int (^sumBlock)(int, int);

 void (^myBlock)();

 int (^myBlock)(int) = ^(int num){ return num *multiplier };

 
 

2> 如何利用block封裝代碼


 ^(int a, int b) {

    return a - b;

 };

 

 ^() {

    NSLog(@"----------");

 };

 

 ^ {

    NSLog(@"----------");

 };



 3> block訪問外面變數

   * block內部可以訪問外面的變數

   * 預設情況下,block內部不能修改外面的局部變數

   * 給局部變數加上__block關鍵字,這個局部變數就可以在block內部修改

  * 給局部變數加上static關鍵字,這個局部變數就可以在block內部修改

 4> 利用typedef定義block類型


 typedef int (^MyBlock)(int, int);

 // 以後就可以利用MyBlock這種類型來定義block變數

 MyBlock block;

 MyBlock b1, b2;

 

 b1 = ^(int a, int b) {

    return a - b;

 };

 

 MyBlock b3 = ^(int a, int b) {

    return a - b;

 };

 

Block使用的細節和本質

1.block實際上是指向結構體的指標

2.編譯器會將block的內部代碼產生對應的函數

Block的記憶體管理 

 1.預設情況下, block的記憶體是在棧中

    * 它不會對所引用的對象進行任何操作

 2.如果對block做一次copy操作, block的記憶體就會在堆中

    * 它會對所引用的對象做一次retain操作

    * 非ARC : 如果所引用的對象用了__block修飾, 就不會做retain操作

    * ARC : 如果所引用的對象用了__unsafe_unretained\__weak修飾, 就不會做retain操作

 

  這裡有一篇個人比較喜歡的關於Block記憶體管理文章,有興趣研究的可以看看:Block 的記憶體管理

Block使用注意

1、在使用block前需要對block指標做判空處理。

 

  不判空直接使用,一旦指標為空白直接產生崩潰。

 


if (!self.isOnlyNet) {

    if (succBlock == NULL) { //後面使用block之前要先做判空處理

        return;

    }

    id data = [NSKeyedUnarchiver unarchiveObjectWithFile:[self favoriteFile]];

    if ([data isKindOfClass:[NSMutableArray class]]) {

        succBlock(data,YES);

    }else{

        succBlock(nil,YES);

    }

}



 

2、使用方將self或成員變數加入block之前要先將self變為__weak

3、在多線程環境下(block中的weakSelf有可能被析構的情況下),需要先將self轉為strong指標,避免在運行到某個關鍵步驟時self對象被析構。

 


__weak __typeof(self)weakSelf = self;

AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {

    __strong __typeof(weakSelf)strongSelf = weakSelf;

    strongSelf.networkReachabilityStatus = status;

    if (strongSelf.networkReachabilityStatusBlock) {

        strongSelf.networkReachabilityStatusBlock(status);

    }

};



Block使用中的一些疑問解答


主要是闡述一下Block中如何的使用外部變數以及block本身的記憶體管理。
 
先定義一個block變數,作為後續的例子中使用:
 

    typedef void(^BlockCC)(void);
    BlockCC _block;

 
1、block中引用外部變數


block中可以直接使用外部的變數,比如
 

    int number = 1;
    _block = ^(){
        NSLog(@"number %d", number);
    };

 
那麼實際上,在block產生的時候,是會把number當做是常量變數編碼到block當中。可以看到,以下的代碼,block中的number值是不會發生變化的:
 

    int number = 1;
    _block = ^(){
        NSLog(@"number %d", number);
    };
    number = 2;
    _block();

則輸出的值為 1,而不是2。原因就是如上所說。
 
如果要在block中嘗試改變外部變數的值,則會報錯的。對於這個問題的解決辦法是引入__block標識符。將需要在block內部修改的變數標識為__block scope。更改後的代碼如下:
 

    __block int number = 1;
    _block = ^(){
        number++;
        NSLog(@"number %d", number);
    };

而這個時候,其實block外部的number和block內部的number指向了同一個值,回到剛才的在外部改變block的例子,它的輸出結果將是2,而不是1。有興趣的可以自己寫一個例子試試。
 
2、block自身的記憶體管理


block本身是像對象一樣可以retain,和release。但是,block在建立的時候,它的記憶體是分配在棧(stack)上,而不是在堆(heap)上。他本身的作於域是屬於建立時候的範圍,一旦在建立時候的範圍外面調用block將導致程式崩潰。比如下面的例子。
我在view did load中建立了一個block:
 

    - (void)viewDidLoad
    {
        [superviewDidLoad];
      
        int number = 1;
        _block = ^(){
      
        NSLog(@"number %d", number);
    };
    }

並且在一個按鈕的事件中調用了這個block:
 

    - (IBAction)testDidClick:(id)sender {
        _block();
    }

此時我按了按鈕之後就會導致程式崩潰,解決這個問題的方法就是在建立完block的時候需要調用copy的方法。copy會把block從棧上移動到堆上,那麼就可以在其他地方使用這個block了~
修改代碼如下:
 

    _block = ^(){
        NSLog(@"number %d", number);
    };
      
    _block = [_blockcopy];

同理,特別需要注意的地方就是在把block放到集合類當中去的時候,如果直接把產生的block放入到集合類中,是無法在其他地方使用block,必須要對block進行copy。不過代碼看上去相對奇怪一些:
 

    [array addObject:[[^{
        NSLog(@"hello!");
    } copy] autorelease]];


3、循環參考


這一點其實是在第一點的一個小的衍生。當在block內部使用成員變數的時候,比如
 

    @interface ViewController : UIViewController
    {
        NSString *_string;
    }
    @end

在block建立中:
 

    _block = ^(){
        NSLog(@"string %@", _string);
    };

這裡的_string相當於是self->_string;那麼block是會對內部的對象進行一次retain。也就是說,self會被retain一次。當self釋放的時候,需要block釋放後才會對self進行釋放,但是block的釋放又需要等self的dealloc中才會釋放。如此一來變形成了循環參考,導致記憶體泄露。
 
修改方案是建立一個__block scope的局部變數,並把self賦值給它,而在block內部則使用這個局部變數來進行取值。因為__block標記的變數是不會被自動retain的。
 

    __block ViewController *controller = self;
    _block = ^(){
        NSLog(@"string %@", controller->_string);
    };

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.