標籤:
Blocks Block 程式碼片段
Block封裝了一段代碼,可以在任何時候執行。它是對C語言中函數的擴充,它實際上是C語言實現的,所以它在各種以C作為基礎的語言哪都是有效,包括Objective-C、C++以及Objective-C++。
Block可以作為函數參數或者函數的返回值,而其本身又可以帶輸入參數或返回值。它和傳統的函數指標很類似,但是有區別:
block是內嵌函式的,並且預設情況下它對局部變數是唯讀。
蘋果官方建議盡量多用block。在多線程、非同步任務、集合遍曆、集合排序、動畫轉場用的很多。
1 定義代碼塊
<returntype>(^blockname)(list of arguments) = ^(arguments){body;};
編譯器可以通過代碼塊的內容推匯出傳回型別,所以可以省略它。如果代碼塊中沒有參數,也可以省略。
int (^Sum)(int, int) = ^(int a, int b){
return a+b;
}
=號前面是代碼塊的定義,而等號後面是實現內容。
定義了一個名叫Sum的block對象,帶有兩個int型參數,返回int。=式右邊是block的具體實現。
2 使用代碼塊將代碼塊聲明成了變數,所以可以像函數一樣使用它Sum(2,22);這行代碼中沒有冪符號,這是因為只有在定義和實現代碼塊的時候才需要使用它,調用時則不需要。
int (^Sum)(int, int) = ^(int a, int b){ NSLog(@"block Sum 被調用了"); return a+b;};NSLog(@"調用Sum之前。");int a = Sum(2,22);NSLog(@"a=%i",a);
輸出結果:
2016-02-25 19:14:46.709 命令列工程[924:132247] 調用Sum之前。2016-02-25 19:14:46.710 命令列工程[924:132247] block Sum 被調用了2016-02-25 19:14:46.710 命令列工程[924:132247] a=24
這樣會固定block,只能使用Sum調用,我們可以通常使用typedef。
3 使用typedef關鍵字
typedef int (^MySum) (int, int);
//聲明了一個block變數,類型為MySum。使用typedef可以聲明無限多個block變數,方便使用。
#import <Foundation/Foundation.h>typedef int (^MySum)(int, int);int main(int argc, const char * argv[]) { @autoreleasepool { MySum sum1 = ^(int a, int b) { return a + b ; }; MySum sum2 = ^(int a, int b) { return a*b; }; NSLog(@"調用sum1 =%i",sum1(10,10)); NSLog(@"調用sum2 =%i",sum2(10,10)); } return 0;}
輸出:
2016-02-25 19:21:22.574 命令列工程[1002:135364] 調用sum1 =202016-02-25 19:21:22.575 命令列工程[1002:135364] 調用sum2 =100Program ended with exit code: 0
4 直接使用代碼塊
使用block通常不需要建立一個代碼塊變數,而是在代碼中內聯代碼塊的內容。
NSArray *stringArray = [NSArray arrayWithObjects:@"any", @"hello", @"body",@"good",@"cool",nil]; //升序排列 NSArray *sortedArray = [stringArray sortedArrayUsingComparator:^(id string1, id string2){ //NSLog(@"string1=%@,string2=%@",string1,string2); return [string1 compare:string2]; }]; NSLog(@"sortArray:%@", sortedArray);
輸出結果:
2016-08-15 14:34:06.257 命令列工程[1478:103112] sortArray:( any, body, cool, good, hello)
5 局部變數
Blocks可以訪問局部變數,但不可以修改。(預設情況下)
#import <Foundation/Foundation.h>typedef int (^MySum)(int, int);int main(int argc, const char * argv[]) { @autoreleasepool { int local = 100; __block int va = 110; MySum sum = ^(int a, int b) { NSLog(@"local = %i,va = %i",local, va); //local++; Variable is not assignable(misssing __block typedef specifier) va++; return a + b +va; }; NSLog(@"調用Sum之前。"); int a = sum(10,10); NSLog(@"調用Sum之後。"); NSLog(@"va = %i",va); NSLog(@"a=%i",a); } return 0;}
輸出:
2016-02-25 19:23:34.909 命令列工程[1028:136661] 調用Sum之前。2016-02-25 19:23:34.910 命令列工程[1028:136661] local = 100,va = 1102016-02-25 19:23:34.910 命令列工程[1028:136661] 調用Sum之後。2016-02-25 19:23:34.910 命令列工程[1028:136661] va = 1112016-02-25 19:23:34.910 命令列工程[1028:136661] a=131Program ended with exit code: 0
由上述例子可以看出局部變數local是不可以在block中改變的,如果修改會警告??:
Variable is not assignable(misssing __block typedef specifier)
而局部變數va是可以修改的,加上__block(兩個底線)。
有些變數無法聲明為__block類型的:
沒有長度可變的數組;
沒有包含可變數組的結構體。
局部變數值
double a1 = 10,b = 20; SampleBlock sample = ^{ return a1*b; }; NSLog(@"%f",sample()); a1 = 20; b = 50; NSLog(@"%f",sample());
結果:
2016-08-15 14:43:44.172 命令列工程[1573:107965] 200.0000002016-08-15 14:43:44.175 命令列工程[1573:107965] 200.000000
我剛開始以為第二個會輸出1000,但是不是。
這是因為變數是局部變數,代碼塊會在定義時複製並儲存它們的狀態,所以兩次輸出結果一樣。
6 全域變數
將上面的變數改為全域變數會是怎樣的結果呢?
static double a1 = 10,b = 20; SampleBlock sample = ^{ return a1*b; }; NSLog(@"%f",sample()); a1 = 20; b = 50; NSLog(@"%f",sample());
結果:
2016-08-15 15:00:22.142 命令列工程[1633:113895] 200.0000002016-08-15 15:00:22.142 命令列工程[1633:113895] 1000.000000
這說明block定義時不會複製全域變數的值。並且block可以更改全域變數的值。
Objective-C 14 代碼塊Block