IOS Block代碼塊的定義與使用

來源:互聯網
上載者:User

標籤:

     代碼塊的本質是和其他的變數類似,不同的是,代碼Block Storage的資料是一個函數體。使用代碼塊,你可以像調用其他標準函數一樣的調用,可以傳入參數,並得到傳回值。
     脫字元是代碼塊的文法標記。表示代碼塊的定義。

1.代碼塊的基本使用

        //無參數無傳回值        void (^myblock)() = ^()        {            NSLog(@"Hello, World!");        };        myblock();                //帶參數無傳回值        void (^myblock2)(NSString *string) = ^(NSString *string){  NSLog(@"%@",string);};        myblock2(@"Hello, World myblock2!");                 //無參數有傳回值        int (^myblocksss)() = ^(int i){return 12;};        int c = myblocksss();        NSLog(@"%i",c);                //有參數有傳回值        int (^myblock3)(int) = ^(int i){ return 12 * i; };        int i = myblock3(3);        NSLog(@"%i",i);

2,利用typedef為Block進行重新命名

使用typedef為block進行一次重新命名,方法跟為函數指標進行重新命名是一樣的:

 

//  Copyright © 2016年 liujun. All rights reserved.//#import <Foundation/Foundation.h>typedef int (^ MyBlock)(int a,int b);int main(int argc, const char * argv[]) {    @autoreleasepool {        // insert code here...                __block int n = 100;                MyBlock block = ^(int a,int b)        {            n = 20; //不過沒有用__block 修飾 代碼不會編譯通過                        return n + a + b;        };                NSLog(@"%i   %i", n ,block(3,4)); //輸出結果  100   27                NSLog(@"%i   %i", block(3,4) ,n);//輸出結果   27    20        //以上輸出。說明代碼塊是在調用的時候才會被執行                NSLog(@"Hello, World!");    }    return 0;}

  

3.Block在記憶體中的位置

根據Block在記憶體中的位置分為三種類型NSGlobalBlock,NSStackBlock, NSMallocBlock。

NSGlobalBlock:類似函數,位於text段;
NSStackBlock:位於棧記憶體,函數返回後Block將無效;
NSMallocBlock:位於堆記憶體。

 

//  Copyright © 2016年 liujun. All rights reserved.//#import <Foundation/Foundation.h>typedef long (^Sum)(int,int);int main(int argc, const char * argv[]) {    @autoreleasepool {        // insert code here...v                Sum sum1 = ^ long (int a, int b) {            return a + b ;        };        NSLog(@"sum1 = %@", sum1);// 列印結果:sum1 = <__NSGlobalBlock__: 0x47d0>                                        int base = 100;        Sum sum2 = ^ long (int a, int b) {            return base + a + b;        };        NSLog(@"sum2 = %@", sum2); // 列印結果:sum2 = <__NSMallocBlock__: 0xbfffddf8>                                Sum sum3 = [sum2 copy];        NSLog(@"sum3 = %@", sum3); // 列印結果:sum3 = <__NSMallocBlock__: 0x902fda0>        NSLog(@"Hello, World!");    }    return 0;}

  NSGlobalBlock,我們只要實現一個沒有對周圍變數沒有引用的Block,就會顯示為是它。而如果其中加入了對定義環境變數的引用,就是NSStackBlock。那麼NSMallocBlock又是哪來的呢?malloc一詞其實大家都熟悉,就是在堆上分配動態記憶體時。沒錯,如果你對一個NSStackBlock對象使用了Block_copy()或者發送了copy訊息,就會得到NSMallocBlock。這一段中的幾項結論可從代碼實驗得出。

     也就得到了下面對block的使用注意點。

     對於Global的Block,我們無需多處理,不需retain和copy,因為即使你這樣做了,似乎也不會有什麼兩樣。對於Stack的Block,如果不做任何操作,就會向上面所說,隨棧幀自生自滅。而如果想讓它獲得比stack frame更久,那就調用Block_copy(),讓它搬家到堆記憶體上。而對於已經在堆上的block,也不要指望通過copy進行“真正的copy”,因為其引用到的變數仍然會是同一份,在這個意義上看,這裡的copy和retain的作用已經非常類似。

4,外部參數在代碼塊的使用

blk1和blk2的區別在於:

blk1沒有使用Block以外的任何外部變數,Block不需要建立局部變數值的快照,這使blk1與一般函數沒有任何區別

blk2與blk1唯一不同是的使用了局部變數base,在定義(注意是“定義”,不是“運行”)blk2時,局部變數base當前值被copy到棧上,作為常量供Block使用。執行下面代碼,結果是203,而不是204。

int base = 100;    base += 100;    BlkSum sum = ^ long (int a, int b) {      return base + a + b;    };    base++;    printf("%ld",sum(1,2));  

 

 在Block內變數base是唯讀,如果想在Block內改變base的值,在定義base時要用 __block修飾:__block int base = 100;

__block int base = 100;  base += 100;  BlkSum sum = ^ long (int a, int b) {    base += 10;    return base + a + b;  };  base++;  printf("%ld\n",sum(1,2));  printf("%d\n",base);

     輸出將是214,211。Block中使用__block修飾的變數時,將取變數此刻運行時的值,而不是定義時的快照。這個例子中,執行sum(1,2)時,base將取base++之後的值,也就是201,再執行Blockbase+=10; base+a+b,運行結果是214。執行完Block時,base已經變成211了。

   

      static變數、全域變數 :如果把上個例子的base改成全域的、或static。Block就可以對他進行讀寫了。因為全域變數或靜態變數在記憶體中的地址是固定的,Block在讀取該變數值的時候是直接從其所在記憶體讀出,擷取到的是最新值,而不是在定義時copy的常量。

static int base = 100;  BlkSum sum = ^ long (int a, int b) {    base++;    return base + a + b;  };  base = 0;  printf("%d\n", base);  printf("%ld\n",sum(1,2)); // 這裡輸出是3,而不是103  printf("%d\n", base);  

  輸出結果是0 4 1,表明Block外部對base的更新會影響Block中的base的取值,同樣Block對base的更新也會影響Block外部的base值。

 

Block變數,被__block修飾的變數稱作Block變數。 基本類型的Block變數等效於全域變數、或靜態變數。

 

5,循環參考

     retain cycle問題的根源在於Block和obj可能會互相強引用,互相retain對方,這樣就導致了retain cycle,最後這個Block和obj就變成了孤島,誰也釋放不了誰。比如:

@implementation TsetBlock    -(id)init{       if (self = [superinit]) {         self.testStr [email protected]"中國";          self.block = ^(NSString *name, NSString *str){             NSLog(@"arr:%@",self.testStr); // 編譯警告:Capturing ‘self‘ strongly in this block is likely to lead to a retain cycle         };     }     returnself;  }  @end  

  網上大部分文章都表述為"block裡面引用了self導致循環參考",但事實真的是如此嗎?我表示懷疑,其實這種說法是不嚴謹的,不一定要顯式地出現"self"字眼才會引起循環參考。我們改一下代碼,不通過屬性self.testStr去訪問String變數,而是通過執行個體變數_testStr去訪問,如下:

@implementation TsetBlock    -(id)init{       if (self = [superinit]) {         self.testStr [email protected]"中國";          self.block = ^(NSString *name,NSString *str){             NSLog(@"arr:%@", _testStr); // 同樣出現: Capturing ‘self‘ strongly in this block is likely to lead to a retain cycle         };     }     returnself;  }  @end  

  可以發現:
即使在你的block代碼中沒有顯式地出現"self",也會出現循環參考!只要你在block裡用到了self所擁有的東西!

要分兩種環境去解決:在ARC下不用__block ,而是用 __weak 為了避免出現循環參考

1.ARC:用__week

__weaktypeof (self)  weakSelf = self; 或者

__weak someClass *weakSelf = self;


2.MRC:用__block ,__block修飾的變數在Block copy時是不會retain的,所以,也可以做到破解循環參考。
__block someClass *blockSelf = self;

 

bloack的 retain、copy、release 操作

對Block不管是retain、copy、release都不會改變引用計數retainCount,retainCount始終是1;

NSGlobalBlock:retain、copy、release操作都無效;
NSStackBlock:retain、release操作無效,必須注意的是,NSStackBlock在函數返回後,Block記憶體將被回收。即使retain也沒用。容易犯的錯誤是[[mutableAarry addObject:stackBlock],在函數出棧後,從mutableAarry中取到的stackBlock已經被回收,變成了野指標。正確的做法是先將stackBlock copy到堆上,然後加入數組:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支援copy,copy之後產生新的NSMallocBlock類型對象。
NSMallocBlock支援retain、release,雖然retainCount始終是1,但記憶體管理器中仍然會增加、減少計數。copy之後不會產生新的對象,只是增加了一次引用,類似retain;
盡量不要對Block使用retain操作。

 

6.代碼塊的遞迴調用

代碼塊想要遞迴調用,代碼塊變數必須是全域變數或者是靜態變數,這樣在程式啟動的時候代碼塊變數就初始化了,可以遞迴調用

 

static void (^ const myblock4)(int) = ^(int i)        {            if(i > 0)            {                NSLog(@"%i",i);                myblock4(i - 1);            }        };

  

      

 

IOS Block代碼塊的定義與使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.