主要內容:c++
1. 開始使用block(Getting Started with Blocks)
2. block概念綜述(Conceptual Overview)
3. 聲明和建立block(Declaring and Creating Blocks)
4. block和變量(Blocks and Variables)
5. 使用blocks(Using Blocks)數組
block介紹
block對象是C-level語句和運行時的特徵,跟標準C函數有些類似;可是除了代碼,它能夠包含棧變量和堆變量。所以,當執行的時候,block能夠維持一組狀態(或者數據)。
開發者能夠將block和函數表達式組合,可傳遞給API或者多線程。block尤爲適用於回調,由於block攜帶了須要執行的的代碼和數據。
block在GCC和Clang都是可用的。開發者可在OS X v10.6或者iOS 4.0以後使用。block runtime是開源代碼。在LLVM’s compiler-rt subprojct repository中找到。block也被帶入到C標準工程組,做爲N1370:Apple’s Extensions to C。由於OC和C++都源自C,因此,block被設計爲三種語言下可工做的(還有Objective-C++),其語法反應這一目標。xcode
開發者閱讀這篇文檔,瞭解block是什麼,並瞭解怎麼在C、C++和Objective-C使用它。安全
1、開始使用block
下面的幾段代碼幫助你開始使用block
數據結構
聲明並使用block
開發者使用^操做符聲明block變量,^表示是一個block的開始。block的body體在{ }以內。例如:多線程
- int multiplier = 7;
- int (^myBlock)(int) = ^(int num) {
- return num * multiplier;
- };
例以下圖所示:app
block可使用「在其定義做用範圍內的」變量;若是你聲明瞭一個block變量,能夠像函數同樣使用它。
框架
- int multiplier = 7;
- int (^myBlock)(int) = ^(int num) {
- return num * multiplier;
- };
-
- printf("%d", myBlock(3));
直接使用block
多數狀況,沒必要要聲明block變量;相反只須要寫一個block代碼(或者翻譯爲文字),在須要參數的地方。下面的例子使用了qsort_b函數。qsort_b跟qsort_r類似的函數,可是使用block做爲參數。dom
- charchar *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
-
- qsort_b(myCharacters, 3, sizeof(charchar *), ^(const voidvoid *l, const voidvoid *r) {
- charchar *left = *(charchar **)l;
- charchar *right = *(charchar **)r;
- return strncmp(left, right, 1);
- });
-
Cocoa和block
好幾個Cocoa框架的函數使用block做爲參數,一般,block也是集合對象的操做或者當作一個回調。下面代碼展現如何在NSArray方法 sortedArrayUsingComparator中使用block。該方法有隻一個block參數。如圖,block被定義爲 NSComparator的本地變量。
- NSArray *stringsArray = @[ @"string 1",
- @"String 21",
- @"string 12",
- @"String 11",
- @"String 02" ];
-
- static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
- NSWidthInsensitiveSearch | NSForcedOrderingSearch;
- NSLocale *currentLocale = [NSLocale currentLocale];
-
- NSComparator finderSortBlock = ^(id string1, id string2) {
-
- NSRange string1Range = NSMakeRange(0, [string1 length]);
- return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
- };
-
- NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];
- NSLog(@"finderSortArray: %@", finderSortArray);
-
__block變量
block的一個強大的功能是,它能夠修飾詞法範圍內的變量。開發者能夠用__block(兩個短下劃線)存儲類型來修飾變量。該一下代碼,你可使用 block變量來計數多少個string被比較。如圖,這種狀況直接使用block並使用currentLocale做爲一個只讀變量。
- NSArray *stringsArray = @[ @"string 1",
- @"String 21",
- @"string 12",
- @"String 11",
- @"Strîng 21",
- @"Striñg 21",
- @"String 02" ];
-
- NSLocale *currentLocale = [NSLocale currentLocale];
- __block NSUInteger orderedSameCount = 0;
-
- NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {
-
- NSRange string1Range = NSMakeRange(0, [string1 length]);
- NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
-
- if (comparisonResult == NSOrderedSame) {
- orderedSameCount++;
- }
- return comparisonResult;
- }];
-
- NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray);
- NSLog(@"orderedSameCount: %d", orderedSameCount);
-
更詳細的討論在下面的【block和變量】。
2、概念綜述
block對象提供一個方法:建立一個ad hoc的功能body體,使用c語言(C派生的語言,好比Objective-C和c++)表達式。在其餘的語言和環境中。block對象有時候被稱之爲「closure」。這裏他們一般被稱做「block」。
block 功能
block是一個匿名的inline代碼集合:
● 參數列表,就像一個函數。
● 有inferred或者聲明的返回類型
● 可得到義
詞法範圍的狀態,。
● 可選擇性修改詞法範圍的狀態。
● 能夠用相同的詞法範圍內定義的其它block共享進行修改的可能性
● 能夠繼續共享和修改狀態的詞法範圍內定義的(棧幀)詞彙範圍(棧幀)已被破壞
開發者能夠copy block甚至傳遞它給其餘線程延期執行。編譯器核運行時系統整理:block引用的變量會被保護。儘管block在純C和C++中能夠,可是block還是OC對象。
使用
block至關的小,自包含的代碼。所以,他們尤爲有用,在封裝可能同時執行的工做。 over items in a collection。或者做爲其餘操做完成的回調。
block是一個較好得替代傳統的callback函數,由於兩個緣由:
1. 容許你把寫代碼寫在調用處,根據函數實現的上下文,該處稍後執行。所以,block常常做爲框架的參數。
2. 容許訪問本地變量。相比於callback須要一個數據結構封裝全部上下文,block中能夠直接訪問本地變量。
3、聲明和建立block
聲明block引用
block變量持有block的引用。開發者聲明他們使用「定義函數指針類似的」語法,須要用^代替*。block和C系統配合很好。
下面都是有效的聲明:
- void (^blockReturningVoidWithVoidArgument)(void);
- int (^blockReturningIntWithIntAndCharArguments)(int, char);
- void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
block仍然支持可變參數(…)。沒有參數的block必須指定void在參數列表。
block被設計爲全類型安全。開發者能夠cast block引用爲任意的指針類型,反過來也同樣。然而,不能將指針轉化成block,所以,block的大小不能在編譯時期計算。
開發者爲block建立type,這樣作一般是最好的作法。當在多個地方須要block簽名
- typedef float (^MyBlockType)(float, float);
-
- MyBlockType myFirstBlock =
- MyBlockType mySecondBlock =
建立一個block
使用^表示block文字表達式的開始。它可能帶()的參數列表跟着。block的body體包含在{}。下面例子定義一個簡單block並把它賦值給一個提早定義好的變量(oneFrom),已C語句結束。
- float (^oneFrom)(float);
-
- oneFrom = ^(float aFloat) {
- float result = aFloat - 1.0;
- return result;
- };
若是你不顯示聲明blcok的返回值,它能夠從該block的內容中自動推斷。若是返回值被推斷,參數列表是void,你也能夠忽略(void)參數列表。若是當多個return語句都存在,它們必須徹底匹配(有必要時使用cast)
全局block
At a file level, 開發者可使用block
- #import <stdio.h>
-
- int GlobalInt = 0;
- int (^getGlobalInt)(void) = ^{ return GlobalInt; };
4、block和變量
該篇文章描述block和變量的相互做用,包括內存管理。
變量類型
你能夠引用三種不一樣類型的變量,就像從一個函數同樣
● 全局變量,包括靜態本地變量
● 全局函數
● 本地變量和參數,從一個封閉的範圍
block也支持另外2兩個類型:
1. 在功能的級別__block變量。他們是block內可變的和被保留,若是有任何的block被賦值到堆中。
2. const imports
最終,在一個方法的實現中。block可能引用OC的對象變量。參考「Object and Block Variables」
block中的變量,有如下五種不一樣的對待,下面的規則使用於block內的變量。
1. 全局變量,能夠訪問,包括封閉
詞法範圍的靜態變量。
2. 傳遞到block的參數是可訪問的(就行函數的參數同樣)。
3. 詞法範圍內的棧變量 ,被捕獲爲const變量。它們的值取在程序中的block表達式的地方。在嵌套block,該值是從最近的封閉範圍內得到。
4. __block儲修飾符聲明的局部變量,是可變的。
任何更改都將反映在封閉的詞法範圍,包括相同封閉的詞法範圍內定義的任何其餘塊。
5. 定義在block範圍內的本地變量,它的行爲很像函數中的局部變。
每一次調用block將拷貝一份局部變量。這些變量能夠反過來被用做常量或經過引用在塊內的封閉塊中的變量。
下面例子闡述了局部變量的用法:
- int x = 123;
-
- void (^printXAndY)(int) = ^(int y) {
-
- printf("%d %d\n", x, y);
- };
-
- printXAndY(456);
注意:試圖給x賦新值將是錯誤的
- int x = 123;
-
- void (^printXAndY)(int) = ^(int y) {
-
- x = x + y;
- printf("%d %d\n", x, y);
- };
容許變量在block中可被訪問,你可使用__block存儲類型修飾符。
__block存儲類型
你能夠指定一個imported的變量是能夠修改的,經過__block存儲類型修飾符。__block存儲跟register、auto和static存儲類型類似(可是之間互斥),用於局部變量。
__block變量駐在存儲(變量的做用域,全部blocks,在變量做用域範圍內聲明或者建立Block 之間共享),所以,這個存儲將會在棧結束被留下來。
做爲優化,block存儲在棧上,就像block自己那樣。若是block被拷貝(經過Block_copy或者copy),變量被拷貝到堆。所以__block變量的地址就可能會改變。
__block變量還有兩個限制,他們不能是變長數組,不能是結構體。
下面例子闡述了__block變量的使用
- __block int x = 123;
-
- void (^printXAndY)(int) = ^(int y) {
-
- x = x + y;
- printf("%d %d\n", x, y);
- };
- printXAndY(456);
下面的例子展現了幾種不一樣的變量
- extern NSInteger CounterGlobal;
- static NSInteger CounterStatic;
-
- {
- NSInteger localCounter = 42;
- __block char localCharacter;
-
- void (^aBlock)(void) = ^(void) {
- ++CounterGlobal;
- ++CounterStatic;
- CounterGlobal = localCounter;
- localCharacter = 'a';
- };
-
- ++localCounter;
- localCharacter = 'b';
-
- aBlock();
-
- }
對象和block變量
block提供了Objective-C和C++對象的支持,和其餘block、變量。
Objective-C 對象
當block被拷貝,它建立了block內變量的強引用。若是你在方法中使用block。
● 若是你訪問實例變量經過引用,強引用self
● 若是你訪問實例變量經過值,強應用該變量。
下面的例子闡述兩種不一樣的狀況:
- dispatch_async(queue, ^{
-
- doSomethingWithObject(instanceVariable);
- });
-
-
- id localVariable = instanceVariable;
- dispatch_async(queue, ^{
-
- doSomethingWithObject(localVariable);
- });
要覆蓋一個特定的對象變量這個問題,您能夠用__block存儲類型修飾符標記。
C++對象
一般能夠在block中使用C++對象。在成員函數中,成員變量的引用和函數須要經過this顯示調用,所以也是可變的。有兩個方面的考慮適用,若是一個block被複制
● 若是你有__block存儲類的class,其將是一個基於堆棧的C++對象,那麼一般的拷貝構造函數。
● 若是您使用任何其餘的C++基於堆棧從一個塊中的對象,它必須有一個const的拷貝構造函數。在C++對象,而後使用該構造函數複製。
block
當你拷貝一個block,從該block任何引用到其餘功能block被複制,若是有必要。整個tree可能被複制。若是你有block變量,你從block中引用一個block,該block將被複制。
5、使用blocks
調用一個block
若是定義一個block變量,你能夠將它做爲一個函數,以下面的例子:
- int (^oneFrom)(int) = ^(int anInt) {
- return anInt - 1;
- };
-
- printf("1 from 10 is %d", oneFrom(10));
-
- float (^distanceTraveled)(float, float, float) =
- ^(float startingSpeed, float acceleration, float time) {
-
- float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
- return distance;
- };
-
- float howFar = distanceTraveled(0.0, 9.8, 1.0);
一般,能夠傳遞一個block做爲函數參數。這些狀況,開發者能夠建立inline的block。
將block做爲函數的參數
開發者傳遞一個block做爲函數參數,就像其餘參數同樣。多數狀況,開發者不用聲明一個block。相反你只須要實現inline的block做爲參數,在須要的地方。下面的方法qsort_b使用block做爲參數。
- charchar *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
-
- qsort_b(myCharacters, 3, sizeof(charchar *), ^(const voidvoid *l, const voidvoid *r) {
- charchar *left = *(charchar **)l;
- charchar *right = *(charchar **)r;
- return strncmp(left, right, 1);
- });
-
- </pre></div><div>注意,該block包含在函數的參數列表中。下一個列子展現了在dispatch_apply函數中使用block,dispatch_apply是這麼定義的:void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); 該函數提交一個塊到調度隊列屢次調用,它有三個參數:第一個指定迭代的數量。第二個參數指定block添加到的隊列。第三是塊自己,這又須要一個參數,當 前迭代的index。您可使用dispatch_apply平凡剛打印出來的迭代指數,以下所 示:</div><div><pre code_snippet_id="114446" snippet_file_name="blog_20131216_17_728540" name="code" class="objc">#include <dispatch/dispatch.h>
- size_t count = 10;
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-
- dispatch_apply(count, queue, ^(size_t i) {
- printf("%u\n", i);
- });
使用block做爲函數參數
Cocoa提供了一系列的使用block的方法。開發者傳一個block做爲參數,就像其餘參數同樣。
下面的例子肯定了「數組中的任意五個元素在給定的set中的索引」。
- NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
- NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil nil];
-
- BOOL (^test)(id obj, NSUInteger idx, BOOLBOOL *stop);
-
- test = ^(id obj, NSUInteger idx, BOOLBOOL *stop) {
-
- if (idx < 5) {
- if ([filterSet containsObject: obj]) {
- return YES;
- }
- }
- return NO;
- };
-
- NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
-
- NSLog(@"indexes: %@", indexes);
-
下面的例子肯定了NSSet對象包含一個單詞,指定本地變量或者被其餘變量賦值。注意found也是__block修飾的。
- __block BOOL found = NO;
- NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil nil];
- NSString *string = @"gamma";
-
- [aSet enumerateObjectsUsingBlock:^(id obj, BOOLBOOL *stop) {
- if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
- *stop = YES;
- found = YES;
- }
- }];
-
拷貝block
通常,開發者不須要copy(或者retain)block。你須要copy當你但願在聲明的範圍被析構後仍使用。copy block到堆上。
能夠copy和release block用C函數
Block_copy();
Block_release();
爲了不內存泄露,開發者必須使用配對使用
Patterns to Avoid
block的文字段(即,^{…})是一個棧的本地數據結構,它表示該塊的地址。所以棧的局部數據結構的範圍是封閉的複合語句,因此你應該避免在下面的例子所示的圖案:
- void dontDoThis() {
- void (^blockArray[3])(void);
-
- for (int i = 0; i < 3; ++i) {
- blockArray[i] = ^{ printf("hello, %d\n", i); };
-
- }
- }
-
- void dontDoThisEither() {
- void (^block)(void);
-
- int i = random():
- if (i > 1000) {
- block = ^{ printf("got i at: %d\n", i); };
-
- }
-
- }