• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            厚積薄發(fā),滴水穿石

            搬家到主站了:http://www.cnblogs.com/cokecoffe/
            隨筆 - 45, 文章 - 8, 評論 - 12, 引用 - 0
            數(shù)據(jù)加載中……

            IOS block 教程

            http://pernghh.pixnet.net/blog/trackback/eac87d412e/33563409

            本文來自臺灣的某開發(fā)人員的博客,被墻,感覺講的比較易懂,所以引過來。文字簡體化了,原來是繁體,變數(shù)=變量,這個注意一下。

             

            本章學習目標:

            1. 了解何謂block。

            2. 了解block的使用方法。

            Block 是iOS在4.0之后新增的程式語法,嚴格來說block的概念并不算是基礎程式設計的范圍,對初學者來說也不是很容易了解,但是在iOS SDK 4.0之后,block幾乎出現(xiàn)在所有新版的API之中,換句話說,如果不了解block這個概念就無法使用SDK 4.0版本以后的新功能,因此雖然block本身的語法有點難度,但為了使用iOS的新功能我們還是得硬著頭皮去了解這個新的程式概念。

            在這一章的目標以了解如何使用block為主而不深入探討block底層的運作方式,至于有些初學者較少遇到的辭匯如「詞法作用域(lexical scope)」等,本章將不再多做解釋,待有興趣的讀者去請教Google大神吧。


            X.1 初探Block

            在這一小節(jié)我們先用一些簡單范例來導入block的概念。

            X.1.1 宣告和使用Block

            我們使用「^」運算子來宣告一個block變數(shù),而且在block的定義最后面要加上「;」來表示一個完整的述句(也就是將整個blo​​ck定義視為前面章節(jié)所介紹的簡單述句,因為整個定義必須是一個完整的句子,所以必須在最后面加上分號),下面是一個block的范例:

               1: int multiplier = 7 ;
               2: int (^myBlock)( int ) = ^( int num)
               3: {
               4:     return num * multiplier;
               5: };

            我們使用下圖來解釋這個范例(請將文字框的字翻譯如下):

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

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

            表示「myBlock」是一個回傳值為整數(shù)(int)的block。

            它有一個參數(shù),型態(tài)也是整數(shù)。

            這個參數(shù)的名字叫做「num」。

            這是block的內(nèi)容。

            值得注意的地方是block可以使用和本身定義范圍相同的變數(shù),可以想像在上面的例子中 multiplier 和 myBlock 都是某一個函數(shù)內(nèi)定義的兩個變數(shù)也就是這個變數(shù)都在某個函數(shù)兩個大括號「{」和「 }」中間的區(qū)塊,因為它們的有效范圍是相同的,因此在block中就可以直接使用 multiplier 這個變數(shù),此外當把block定義成一個變數(shù)的時,我們可以直接像使用一般函數(shù)般的方式使用它:

               1: int multiplier = 7 ;
               2: int (^myBlock)( int ) = ^( int num)
               3: {
               4:     return num * multiplier;
               5: };
               6: printf ( "%d" , myBlock( 3 ));
               7: //結果會打印出21

            X.1.2 直接使用Block

            在很多情況下,我們并不需要將block宣告成變數(shù),反之我們可以直接在需要使用block的地方直接用內(nèi)嵌的方式將block的內(nèi)容寫出來,在下面的例子中qsort_b函數(shù),這是一個類似傳統(tǒng)的qsort_t函數(shù),但是直接使用block做為它的參數(shù):

               1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" };
               2: qsort_b (myCharacters, 3 ,
               3:          sizeof ( char *),
               4:          ^( const void *l, const void *r)//block部分
               5:             {
               6:                 char *left = *( char **)l;
               7:                 char *right = *( char **)r;
               8:                 return strncmp (left, right, 1 );
               9:             }                            //end
              10: );

            X.1.3 __block 變量

            一般來說,在block內(nèi)只能讀取在同一個作用域的變數(shù)而且沒有辦法修改在block外定義的任何變數(shù),此時若我們想要這些變數(shù)能夠在block中被修改,就必須在前面掛上__block的修飾詞,以上面第一個例子中的 multiplier 來說,這個變數(shù)在 block 中是唯讀的,所以 multiplier = 7 指定完后,在 block 中的 multiplier 就只能是 7 不能修改,若我們在 block 中修改 multiplier ,在編輯時就會產(chǎn)生錯誤,因此若想要在 block 中修改 multiplier ,就必須在 multiplier 前面加上 __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:                         };



            X.2 Block 概要

             

            Block 提供我們一種能夠?qū)⒑瘮?shù)程式碼內(nèi)嵌在一般述句中的方法,在其他語言中也有類似的概念稱做「closure」,但是為了配合Objective-C的貫例,我們一律將這種用法稱為「block」

            X.2.1 Block 的功能

            Block 是一種具有匿名功能的內(nèi)嵌函數(shù),它的特性如下:

            如一般的函數(shù)般能擁有帶有型態(tài)的參數(shù)。

            擁有回傳值。

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

            可以選擇性地修改詞法作用域的狀態(tài)。

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

            我們可以拷貝一個block,也可以將它丟到其他的執(zhí)行緒中使用,基本上雖然b​​lock在iOS程式開發(fā)中可以使用在C/C++開發(fā)的程式片段,也可以在Objective-C中使用,不過在系統(tǒng)的定義上,block永遠會被視為是一個Objective-C的物件。

            X.2.2 Block 的使用時機

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

            在新的iOS API中block被大量用來取代傳統(tǒng)的delegate和callback,而新的API會大量使用block主要是基于以下兩個原因:

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

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

             


            X.3 宣告和建立Block

             

            X.3.1 宣告Block的參考(Reference)

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

               1: /* 回傳void ,參數(shù)也是void 的block*/
               2: void (^blockReturningVoidWithVoidArgument)( void );
               3: /* 回傳整數(shù),兩個參數(shù)分別是整數(shù)和字元型態(tài)的block*/
               4: int   (^blockReturningIntWithIntAndCharArguments)( int , char );
               5: /* 回傳void ,含有10 個block 的陣列,每個block 都有一個型態(tài)為整數(shù)的參數(shù)*/
               6: void (^arrayOfTenBlocksReturningVoidWinIntArgument[ 10 ])( int );
               7: X.3.2 建立一個Block 
               8:  
               9: 我們使用「^」來開始一個block,并在最后使用「;」來表示結束,下面的范例示范了一個block變數(shù),然后再定義一個block把它指定給block變數(shù): 
              10:  
              11: int (^oneFrom)( int ); /* 宣告block 變數(shù)*/
              12:     /* 定義block 的內(nèi)容并指定給上面宣告的變數(shù)*/
              13:     oneFrom = ^(int anInt)
              14:                 {
              15:                     return anInt = - 1 ; 
              16:                 };

            X.3.3 全域的Block

            我在可以在檔案中宣告一個全域的block,請參考以下范例:

               1: int GlobalInt = 0 ;
               2: int (^getGlobalInt)( void ) = ^ ( void ) { return GlobalInt ;};

             


            X.4 Block 和變量

             

            接下來的這一小節(jié)我們將會介紹block和變數(shù)之間的互動。

            X.4.1 變數(shù)的型態(tài)

            我們可以在block中遇到平常在函數(shù)中會遇到的變數(shù)類型:

            l 全域(global)變數(shù)或是靜態(tài)的區(qū)域變數(shù)(static local)。

            l 全域的函數(shù)。

            l 區(qū)域變數(shù)和由封閉領域(enclosing scope)傳入的參數(shù)。

            除了上述之外block額外支援了另外兩種變數(shù):

            在函數(shù)內(nèi)可以使用__block 變數(shù),這些變數(shù)在block中是可被修改​​的。

            匯入常數(shù)(const imports)。

            此外,在方法的實作里,block可以使用Objective-C的實體變數(shù)(instance variable)。

            下列的規(guī)則可以套用到在block中變數(shù)的使用:

            可以存取全域變數(shù)和在同一領域(enclosing lexical scope)中的靜態(tài)變數(shù)。

            可以存取傳入block的參數(shù)(使用方式和傳入函數(shù)的參數(shù)相同)。

            在同一領域的區(qū)域變數(shù)在block中將視為常數(shù)(const)。

            可以存取在同一領域中以__block 為修飾詞的變數(shù)。

            在block中宣告的區(qū)域變數(shù),使用方式和平常函數(shù)使用區(qū)域變數(shù)的方式相同。

            下面的例子介紹了區(qū)域變數(shù)(上述第三點)的使用方式:

               1: int x = 123 ;
               2: void (^printXAndY)( int ) = ^( int y)
               3:     {
               4: printf ( "%d %d\n" , x, y); 
               5:     };
               6: // 將會印出123 456
               7:     printXAndY( 456 );
               8: 就如上面第三點所提到的,在上例中的int x = 123的變量x,在傳入block后將視同常數(shù),因此若我們在block中試著去修改x的值時就會產(chǎn)生錯誤,下面的例子將會無法通過編譯: 
               9:  
              10: int x = 123 ;
              11: void (^printXAndY)( int ) = ^( int y)
              12: {
              13:     // 下面這一行是錯的,因為x 在這是一個常數(shù)不能被修改。
              14:     x = x + y;
              15:     printf ( "%d %d\n" , x, y); 
              16:     };

            若在block中想要修改上面的變數(shù)x,必須將x宣告加上修飾詞__block,請參考接下來這一小節(jié)的介紹。

            X.4.2 __block 型態(tài)變數(shù)

            我們可以藉由將一個由外部匯入block的變數(shù)放上修飾詞__block來讓這個變數(shù)由唯讀變成可以讀和寫,不過有一個限制就是傳入的變數(shù)在記憶體中必須是一個占有固定長度記憶體的變數(shù),__block修飾詞無法使用于像是變動長度的陣列這類不定長度的變數(shù)​​,請參考下面的范例:

               1: // 加上__block 修飾詞,所以可以在block 中被修改。
               2: __block int x = 123 ;
               3: void (^printXAndY)( int ) = ^( int y)
               4:     {
               5:         x = x + y; 
               6: printf ( "%d %d\n" , x, y); 
               7:     };
               8: // 將會印出579 456
               9:     printXAndY( 456 );
              10: //x 將會變成 579;
              11: 下面我們使用一個范例來介紹各類型的變數(shù)和block之間的互動: 
              12:  
              13: extern NSInteger CounterGlobal;
              14: static NSInteger CounterStatic;
              15: {
              16: NSInteger localCounter = 42 ;
              17: __block char localCharacter;
              18: void (^aBlock)( void ) = ^( void )
              19:     {
              20:         ++ CounterGlobal ; //可以存取。
              21:         ++ CounterStatic ; //可以存取?!?/span>
              22: CounterGlobal = localCounter; //localCounter在block 建立時就不可變了。
              23:         localCharacter = 'a' ; //設定外面定義的localCharacter 變數(shù)。
              24:     };
              25:     ++localCounter; //不會影響的block 中的值。
              26:     localCharacter = 'b' ;
              27:     aBlock(); //執(zhí)行block 的內(nèi)容。
              28: //執(zhí)行完后,localCharachter 會變成'a'
              29: }

            X.4.3 物件和Block變數(shù)

            Block 支援在Objective-C、C++物件和其他block中當作變數(shù)來使用,不過因為在大部分的情況我們都是使用Objective-C的撰寫程式,因此在這一小節(jié)我們僅針對Objective-C的情況進行介紹,至于其他兩種情況就留給有興趣的讀者再自行深入研究了。

            x.4.3.1 Objective-C 物件

            在擁有參考計數(shù)(reference-counted)的環(huán)境中,若我們在block中參考到Objective-C的物件,在一般的情況下它將會自動增加物件的參考計數(shù),不過若以__block為修飾詞的物件,參考計數(shù)則是不受影響。

            如果我們在Objective-C的方法中使用block時,以下幾個和記憶體管理的事是需要額外注意的:

            l 若直接存取實體變數(shù)(instance variable),self的參考計數(shù)將被加1。

            l 若透過變數(shù)存取實體變數(shù)的值,則只變數(shù)的參考計數(shù)將被加1。

            以下程式碼說明上面兩種情況,在這個假設instanceVariable是實體變數(shù):

               1: dispatch_async (queue, ^{
               2: // 因為直接存取實體變數(shù)instanceVariable ,所以self 的retain count 會加1
               3: doSomethingWithObject (instanceVariable);
               4:     });
               5: id localVaribale = instanceVariable;
               6: dispatch_async (queue, ^{
               7: //localVariable 是存取值,所以這時只有l(wèi)ocalVariable 的retain count 加1
               8: //self 的 return count  并不會增加。
               9: doSomethingWithObject (localVaribale);
              10:     });


            X.5 使用Block

            這一小節(jié)我們將會對block的使用方式做一些初步的介紹

            X.5.1 呼叫一個Block

            當block宣告成一個變數(shù)時,我們可以像使用一般函數(shù)的方式來使用它,請參考下面兩個范例:

               1: int (^oneFrom)( int ) = ^( int anInt) {
               2: return anInt - 1 ;
               3:     };
               4: printf ( "1 from 10 is %d" , oneFrom( 10 ));
               5: //結果會顯示:1 from 10 is 9
               6: float (^distanceTraveled)( float , float , float ) = ^( float startingSpeed, float acceleration, float time)
               7:     {
               8: float distance = (startingSpeed ​​* time) + ( 0.5 * acceleration * time * time);
               9: return distance;
              10:     };
              11: float howFar = distanceTraveled( 0.0 , 9.8 , 1.0 );
              12: //howFar會變成4.9

            在一般常見的情況中,若是將block當做是參數(shù)傳入函數(shù),我們通常會使用「內(nèi)嵌」的方式來使用block。

            X.5.2 將Block當作函數(shù)的參數(shù)

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

               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本身就是函數(shù)參數(shù)的一部分,在下一個例子中dispatch_apply函數(shù)中使用block,dispatch_apply的定義如下:

               1: void
               2: dispatch_apply( size_t iterations, dispatch_queue_t queue, void (^block)( size_t ));
               3: 這個函數(shù)將一個block提交到發(fā)送佇列(dispatch queue)中來執(zhí)行多重的呼叫,只有當佇列中的工作都執(zhí)行完成后才會回傳,這個函數(shù)擁有三個變數(shù),而最后一個參數(shù)就是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:     });

            X.5.3 將Block當作方法的參數(shù)

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

               1: // 所有的資料
               2: NSArray *array = [ NSArray arrayWithObjects : @"A" , @"B" , @"C" , @"A" , @"B" , @"Z" , @"G" , @"are" , @" Q" ,nil ];   
               3: // 我們只要這個集合內(nèi)的資料
               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
            X.5.4 該避免的使用方式

            在下面的例子中,block是for回圈的區(qū)域變數(shù)因此在使用上必須避免將區(qū)域的block指定給外面宣告的block:

               1: // 這是錯誤的范例,請勿在程式中使用這些語法!!
               2: void dontDoThis() {
               3:     void (^blockArray[3])(void); // 3 個block 的陣列
               4:     for (int i = 0; i < 3; ++i) {
               5:         blockArray[i] = ^{ printf("hello, %d\n", i); };
               6:         // 注意: 這個block 定義僅在for 回圈有效。
               7:     }
               8: }
               9: void dontDoThisEither() {
              10:     void (^block)(void);
              11:     int i = random():
              12:     if (i > 1000) {
              13:         block = ^{ printf("got i at: %d\n", i); };
              14:         // 注意: 這個block 定義僅在if 后的兩個大括號中有效。
              15:     }
              16:     // ...
              17: }

            posted on 2012-05-31 13:47 Wangkeke 閱讀(23815) 評論(3)  編輯 收藏 引用 所屬分類: IOS

            評論

            # re: IOS block 教程  回復  更多評論   

            為什么我縮進后 顯示出來的都是斜體呢?各位有知道的不?
            2012-05-31 17:09 | Wangkeke

            # re: IOS block 教程  回復  更多評論   

            不錯挖,很精彩。
            2013-03-01 09:58 | jickalice

            # re: IOS block 教程  回復  更多評論   

            "X.5.4 該避免的使用方式"這一部分寫的有問題,下面的代碼都是可以編譯通過并運行的:
            void (^blocks[3])(void);
            for (NSInteger i = 0; i < 3; i++) {
            blocks[i] = ^{
            NSLog(@"Hello:%i", i);
            };
            }

            blocks[0]();
            blocks[1]();
            blocks[2]();

            void (^block)(void);
            if (YES) {
            block = ^{
            NSLog(@"Test");
            };
            }
            block();

            在OBJC中,Block就是對象,Block對象的作用域跟NSString, NSNumber對象等沒有區(qū)別,只要在Block對象的作用域中,對Block的操作就不會出問題。 對于Block,需要注意的大概就是__block這個關鍵字,以及Block靈活的用法。
            2013-06-17 14:19 | Yongbin
            欧美激情精品久久久久久久| 中文字幕无码av激情不卡久久| 久久天天躁夜夜躁狠狠躁2022| 久久亚洲精品无码播放| 偷偷做久久久久网站| 国产激情久久久久久熟女老人| 欧美亚洲日本久久精品| AV无码久久久久不卡蜜桃| 久久久av波多野一区二区| 久久99国产精品99久久| 欧美精品丝袜久久久中文字幕 | 久久91亚洲人成电影网站| 久久久久久极精品久久久| 亚洲午夜久久久久久久久久| 久久午夜电影网| 亚洲午夜精品久久久久久app| 久久国产欧美日韩精品| 香蕉久久永久视频| 99精品久久久久久久婷婷| 久久综合九色综合网站| 国产精品99久久久久久www| 久久国产色AV免费观看| 亚洲国产精品无码久久久秋霞2| 久久久久久av无码免费看大片| 亚洲成色www久久网站夜月| 无夜精品久久久久久| 国产精品九九久久免费视频 | 久久久久黑人强伦姧人妻| A狠狠久久蜜臀婷色中文网| 色狠狠久久综合网| 国产精品gz久久久| 91精品国产色综久久 | 精品久久久久久国产三级| 亚洲精品乱码久久久久久自慰| 要久久爱在线免费观看| 亚洲国产成人精品久久久国产成人一区二区三区综 | 国产精品美女久久久久av爽| 久久精品草草草| 色综合久久88色综合天天| 国产精品久久影院| 久久精品一区二区|