IOS中 Block簡介與用法(一)

憶江南的部落格發表於2015-10-14

http://blog.csdn.net/enuola/article/details/8674063

Block簡介:

Block的實際行為和Function很像,最大的差別是在可以存取同一個Scope的變數值。Block實體形式如下:

^(傳入引數列){行為主體};

Block實體開頭是“^”,接著是由小括號所包起來的引數列(比如 int a, int b, int c),行為主體由大括號包起來,專有名字叫做block literal。行為主體可以用return回傳值,型別會被compiler自動辨別。如果沒有引數列要寫成: ^(void)。

例如下面的一個例子:

  1. ^(int a){return a*a;};  

這是代表Block會回傳輸入值的平方值(int a 就是引數列 return a*a; 就是行為主體)。記得行為主體裡最後要加“;”,因為是敘述,而整個{}最後也要加“;”,因為Block是物件實體。用法如下:

  1. int result = ^(int a){return a*a;}(5);  
  2. NSLog(@"%d", result);  

很奇怪吧?後面的小括號裡面的5會被當成a的輸入值,然後經由Block輸出 5*5 = 25指定給result這個變數。

有沒有簡單一點的方法嗯?不然每次都寫這麼長?有。接下來介紹一個叫做Block Pointer的東西來簡化我們的寫法。

Block Pointer是這樣定義的:

回傳值(^名字)(引數列);

比如下面的例子:

  1. //宣告一個square的Block Pointer,其所指向的Block有一個int輸入和int輸出  
  2. int (^square)(int);  
  3. //將Block實體指定給square  
  4. square = ^(int a){ return a*a ; };  
  5. //呼叫方法,感覺是是不是很像function的用法?  
  6. int result = square(5);  
  7. NSLog(@"%d", result);  
是不是變的簡單了?

也可以吧Block Pointer當成引數傳遞給一個function,比如:

  1. void myFunction(int (^mySquare)(int));     //function的定義,將Block作為引數  
  2. int (^mySquare)(int) = ^(int a){return a*a;};   //定義一個mySquare的Block pointer變數  
  3. myFunction(mySquare);    //把mySquare作為myFunction的引數  

上面的三行程式碼其實等價於下面這行程式碼:

  1. myFunction( ^int(int a){return a*a;} );  

當其作為Object-C method的傳入值的話,需要把型別寫在變數前面,然後加上小括號。比如下面這種寫法:

  1. -(void)objcMethod:(int(^)(int))square;  //square引數的型別是int(^)(int)  

存取變數

1、可以讀取和Block pointer同一個Scope的變數值:

  1. {  
  2.     int outA = 8;  
  3.     int (^myPtr)(int) = ^(int a){ return outA + a;};  
  4.     //block裡面可以讀取同一型別的outA的值  
  5.     int result = myPtr(3);  // result is 11  
  6.     NSLog(@"result=%d", result);  
  7. }  

下面來看一段很有意思的程式碼:

  1. {  
  2.     int outA = 8;  
  3.     int (^myPtr)(int) = ^(int a){ return outA + a;}; //block裡面可以讀取同一型別的outA的值  
  4.       
  5.     outA = 5;  //在呼叫myPtr之前改變outA的值  
  6.     int result = myPtr(3);  // result的值仍然是11,並不是8  
  7.     NSLog(@"result=%d", result);  
  8. }  

為什麼result 的值仍然是11?而不是8呢?事實上,myPtr在其主體中用到的outA這個變數值的時候做了一個copy的動作,把outA的值copy下來。所以,之後outA即使換成了新的值,對於myPtr裡面copy的值是沒有影響的。

需要注意的是,這裡copy的值是變數的值,如果它是一個記憶體的位置(地址),換句話說,就是這個變數是個指標的話,

它的值是可以在block裡被改變的。如下例子:

  1. {  
  2.     NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"one", @"two", @"three", nil];  
  3.     int result = ^(int a){[mutableArray removeLastObject]; return a*a;}(5);  
  4.     NSLog(@"test array :%@", mutableArray);  
  5. }  

原本mutableArray的值是{@"one",@"two",@"three"},在block裡面被更改mutableArray後,就變成{@"one"@"two"}了。

2、直接存取static型別的變數

  1. {  
  2.     static int outA = 8;  
  3.     int (^myPtr)(int) = ^(int a){return outA + a;};  
  4.     outA = 5;  
  5.     int result = myPtr(3);  //result的值是8,因為outA是static型別的變數  
  6.     NSLog(@"result=%d", result);  
  7.       
  8. }  

甚至可以直接在block裡面修改outA的值,例如下面的寫法:

  1. {  
  2.     static int outA = 8;  
  3.     int (^myPtr)(int) = ^(int a){ outA = 5; return outA + a;};  
  4.     int result = myPtr(3);  //result的值是8,因為outA是static型別的變數  
  5.     NSLog(@"result=%d", result);  
  6.       
  7. }  

3、Block Variable型別的變數

在某個變數前面如果加上修飾字“__block”的話(注意,block前面有兩個下劃線),這個變數就稱作block variable。

那麼在block裡面就可以任意修改此變數的值,如下程式碼:

  1. {  
  2.     __block int num = 5;  
  3.       
  4.     int (^myPtr)(int) = ^(int a){return num++;};  
  5.     int (^myPtr2)(int) = ^(int a){return num++;};  
  6.     int result = myPtr(0);   //result的值為5,num的值為6  
  7.     result = myPtr2(0);      //result的值為6,num的值為7  
  8.     NSLog(@"result=%d", result);    
  9.   
  10. }  

因為myPtr和myPtr2都有用到num這個block variable,最終num的值為7.

未完待續。。


相關文章