Block可以協助我們組織獨立的程式碼片段,並提高複用性和可讀性。iOS4在UIKit中引入了該特徵。超過100個的Apple API都使用了Block,所以這是一個我們必須開始熟悉的知識。
Block是什麼樣的?
你可以使用^操作符來聲明一個Block變數,它表示一個Block的開始。
- int num1 = 7;
- int(^aBlock)(int) = ^)int num2) {
- return num1+nunm2;
- };
在如上代碼中我們將Block聲明為一個變數,所以可以將它當做一個函數中使用:
- NSLog(@"%d", aBlock(49)); //adds 49 to 7 which gives us 56.
我們剛看過了將block當做變數的情況,但通常情況下我們會以內聯的方式使用Block,比如在一個變數中。API要麼會使用Block在一個對象集合上執行某種操作,要麼將其作為一個操作完成後的回調。
- NSComperator compareStringsBlock = ^(id stringA, id stringB) {
- NSRange rangeS = NSMakeRange (0, [stringA length]);
- return (stringA compare:stringB options:comparisonOptions range:rangeS locale:currentLocale];
- };
-
- NSArray *compareSortArray = [arrayOfStringDays sortArrayUsingComparator: compareStringsBlock]);
Block具有將臨時函數體建立為運算式的優勢。Apple文檔中指出:
Block是符合如下要求的匿名內聯的代碼集:
- 和函數一樣具有一個指定類型的參數列表
- 有一個可以推導或聲明的傳回值類型
- 可以從它被定義的詞義範圍中捕捉狀態
- 可以在需要的時候改變詞義範圍的狀態
- 可以和相同的詞義範圍中定義的其他的Block共用更改的可能。
- 可以在詞義範圍(堆疊框架)被銷毀後繼續共用和修改該詞義範圍(堆疊框架)的狀態。
Block是一個自包含的小程式碼片段,封裝了用於遍曆(線性遍曆)或者回調,可以並發執行的任務單元。
聲明和使用Block
Apple文檔中介紹了如何將一個Block聲明為變數,並將其作為一個函數使用:
- int (^oneFrom)(int) = ^(int anInt) {
- return anInt - 1;
- };
- // 我們建立了一個內聯塊^(int anInt)... ,其函數體和結果被傳到了另外一個名為OneFrom的Block。
-
- printf("1 from 10 is %d", oneFrom(10));
- // 列印出: "1 from 10 is 9"
- // 這個block函數(distanceTraveled)傳入3個float型參數,返回float值。
-
- float (^distanceTraveled) (float, float, float) =
-
- ^(float startingSpeed, float acceleration, float time) {
- float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
- return distance;
- };
你也可以傳入一個Block作為一個參數,而不要以如上的方式聲明它們,這樣就可以在需要將block作為參數的時候以內聯代碼的方式簡單地實現。
- NSArray *anArray = [NSArray arrayWithObjects: @"cat", @"dog",nil];
- sortFunction(anArray, ^(string *a string *b){
- if ( a == @"cat") return TRUE; });
這樣我們就看到一個內聯的block程式碼片段佔據了最後一個參數(必須是參數列表的最後一個參數)的位置。Cocoa提供了很多使用Block的方法,這樣你就可以傳入Block作為方法的參數:
- NSArray *array = [NSArray arrayWithObjects: @"A", @"B", @"C", nil];
- NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
-
- BOOL (^test)(id obj, NSUInteger idx, BOOL *stop); //Block declaration returns BOOL, params inc. id and BOOL
- //body of block gets the block literal ^(id obj, NSUInteger idx, Bool *stop)... and the body logic
- test = ^ (id obj, NSUInteger idx, BOOL *stop) {
- if (idx < 5) {
- if ([filterSet containsObject: obj]) {
- return YES;
- }
- }
- return NO;
-
- };
Apple提供的另外一個例子是:
- __block BOOL found = NO;
- NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
- NSString *string = @"gamma";
- //we provide below a way of how to enumerate, using our own compare logic
- [aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
- if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
- *stop = YES;
- found = YES;
- }
- }];
As you can see, it takes a little while to have it sink in but once you get it, it's quite simple. I suggest looking at Apple's documentation, as well as looking at the referenced APIs to see how they are used. Practice makes perfect.
原文出處:http://answers.oreilly.com/topic/2281-how-to-use-blocks-with-ios/