IOS中Block用法介紹

IOS中Block用法介紹


1 什麼是block

Block iOS4.0之後新增的程式語法,嚴格來說block的概念並不算是基礎程式設計的範圍.BlockC級別的語法和運行時特性。Block比較類似C函數,但是Block比之C函數,其靈活性體現在棧內存、堆內存的引用,我們甚至可以將一個Block作爲參數傳給其他的函數或者Block

引用網上例子:

  1: int multiplier = 7 ;

   2: int (^myBlock)( int ) = ^( int num)

   3: {

   4:     return num * multiplier;

   5: };


語法介紹:

我們使用「^」運算子來宣告一個block變數,而且在block的定義最後面要加上「;」來表示一個完整的述句.

我們宣告一個「myBlock」變數,用「^」符號來表示這是一個block

這是block的完整定義,這個定義將會指定給「myBlock」變數。

表示「myBlock」是一個回傳值爲整數(int)的block

它有一個參數,型態也是整數。

這個參數的名字叫做「num」。

這是block的內容。


  1. typedef void (^BoolBlock)(BOOL);//一個只接受一個BOOL參數,沒有返回值的block 
  2. typedef int (^IntBlock)(void);//一個沒有參數,返回intblock 
  3. typedef BoolBlock (^HugeBlock)(IntBlock);//看看,這個HugeBlock的參數和返回值都是block 

  1. - (void)someMethod 
  2.     BoolBlock ablock = ^(BOOL bValue) { 
  3.         NSLog(@"Bool block!"); 
  4.     }; 
  5.     ablock(); 


 __block 變量:

block內只能讀取在同一個作用域的變數而且沒有辦法修改在block外定義的任何變數,此時若我們想要這些變數能夠在block中被修改,就必須在前面掛上__block的修飾詞,


1: __block int multiplier = 7 ;

   2: int (^myBlock)( int ) = ^( int num)

   3:                         {

   4:                             if (num > 5 )

   5:                             {

   6:                                   multiplier = 7 ;

   7:                             }

   8:                             else

   9:                             {

  10:                                   multiplier = 10 ;

  11:                             }

  12:                             return num * multiplier;

  13:                         };

 

Block 概要

 

Block 提供我們一種能夠將函數程式碼內嵌在一般述句中的方法,在其他語言中也有類似的概念稱做「closure」,但是爲了配合Objective-C的貫例,我們一律將這種用法稱爲「block

Block 的功能

Block 是一種具有匿名功能的內嵌函數,它的特性如下:

如一般的函數般能擁有帶有型態的參數。

擁有回傳值。

可以擷取被定義的詞法作用域(lexical scope)狀態。

可以選擇性地修改詞法作用域的狀態。

注:詞法作用域(lexical scope)可以想像成是某個函數兩個大括號中間的區塊,這個區塊在程式執行時,系統會將這個區塊放入堆疊記憶體中,在這個區塊中的宣告的變數就像是我們常聽到的區域變數,當我們說block可以擷取同一詞法作用域的狀態時可以想像block變數和其他區域變數是同一個層級的區域變數(位於同一層的堆疊裏),而block的內容可以讀取到和他同一層級的其他區域變數。

我們可以拷貝一個block,也可以將它丟到其他的執行緒中使用,基本上雖然blockiOS程式開發中可以使用在C/C++開發的程式片段,也可以在Objective-C中使用,不過在系統的定義上,block永遠會被視爲是一個Objective-C的物件。


Block 的使用時機

Block 一般是用來表示、簡化一小段的程式碼,它特別適合用來建立一些同步執行的程式片段、封裝一些小型的工作或是用來做爲某一個工作完成時的回傳呼叫(callback 

在新的iOS APIblock被大量用來取代傳統的delegatecallback,而新的API會大量使用block主要是基於以下兩個原因:

可以直接在程式碼中撰寫等會要接着執行的程式,直接將程式碼變成函數的參數傳入函數中,這是新API最常使用block的地方。

可以存取區域變數,在傳統的callback實作時,若想要存取區域變數得將變數封裝成結構才能使用,而block則是可以很方便地直接存取區域變數。


宣告和建立Block

 

宣告Block的參考(Reference

Block 變數儲存的是一個block的參考,我們使用類似宣告指標的方式來宣告,不同的是這時block變數指到的地方是一個函數,而指標使用的是「*」,block則是使用「^」來宣告,下面是一些合法的block宣告:

   1: /* 回傳void ,參數也是void 的block*/

    2: void (^blockReturningVoidWithVoidArgument)( void );

   3: /* 回傳整數,兩個參數分別是整數和字元型態的block*/

   4: int   (^blockReturningIntWithIntAndCharArguments)( int , char );

   5: /* 回傳void ,含有10 個block 的陣列,每個block 都有一個型態爲整數的參數*/

   6: void (^arrayOfTenBlocksReturningVoidWinIntArgument[ 10 ])( int );

   7: X.3.2 建立一個Block 

   9: 我們使用「^」來開始一個block,並在最後使用「;」來表示結束,下面的範例示範了一個block變數,然後再定義一個block把它指定給block變數: 

  10:  

  11: int (^oneFrom)( int ); /* 宣告block 變數*/

  12:     /* 定義block 的內容並指定給上面宣告的變數*/

  13:     oneFrom = ^(int anInt)

  14:                 {

  15:                     return anInt = - 1 ; 

  16:                 };



Block當作函數的參數

我們可以像使用一般函數使用參數的方式,將block以函數參數的型式傳入函數中,在這種情況下,大多數我們使用block的方式將不會傾向宣告block而是直接以內嵌的方式來將block傳入,這也是目前新版SDK中主流的做法,我們將補充前面章節的例子來說明:

   1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" };

   2: qsort_b (myCharacters, 3 , sizeof ( char *),

   3:             ^( const void *l, const void *r)

   4:             {

   5: char *left = *( char **)l;

   6: char *right = *( char **)r;

   7: return strncmp (left, right, 1 );

   8:             } // 這裏是block 的終點。

   9:             );

  10: // 最後的結果爲:{"Charles Condomine", "George", "TomJohn"}

在上面的例子中,block本身就是函數參數的一部分,在下一個例子中dispatch_apply函數中使用blockdispatch_apply的定義如下:

    1: void

   2: dispatch_apply( size_t iterations, dispatch_queue_t queue, void (^block)( size_t ));

   3: 這個函數將一個block提交到發送佇列(dispatch queue)中來執行多重的呼叫,只有當佇列中的工作都執行完成後纔會回傳,這個函數擁有三個變數,而最後一個參數就是block ,請參考下面的範例: 

   4:  

   5: size_t count = 10 ;

   6: dispatch_queue_t queue =

   7: dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );

   8: dispatch_apply (count, queue, ^( size_t i) {

   9: printf ( "%u\n" , i);

  10:     });

Block當作方法的參數

SDK中提供了許多使用block的方法,我們可以像傳遞一般參數的方式來傳遞block,下面這個範例示範如何在一個陣列的前5筆資料中取出我們想要的資料的索引值:

   1: // 所有的資料

   2: NSArray *array = [ NSArray arrayWithObjects : @"A" , @"B" , @"C" , @"A" , @"B" , @"Z" , @"G" , @"are" , @" Q" ,nil ];   

   3: // 我們只要這個集合內的資料

   4: NSSet *filterSet = [ NSSet setWithObjects : @"A" , @"B" , @"Z" , @"Q" , nil ];

   5: BOOL (^test)( id obj, NSUInteger idx, BOOL *stop);

   6: test = ^ ( id obj, NSUInteger idx, BOOL *stop) {

   7: // 只對前5 筆資料做檢查

   8: if (idx < 5 ) {

   9: if ([filterSet containsObject : obj]) {

  10: return YES ;

  11:               }

  12:       }

  13: return NO ;

  14: };

  15: NSIndexSet *indexes = [array indexesOfObjectsPassingTest :test];

  16: NSLog ( @"indexes: %@" , indexes);   

  17: // 結果:indexes: <NSIndexSet: 0x6101ff0>[number of indexes: 4 (in 2 ranges), indexes: (0-1 3-4)]

  18: // 前5筆資料中,有4筆符合條件,它們的索引值分別是0-1, 3-4


發佈了38 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章