其實一直不然懂block這個東西,今天就 一邊查資料 一邊記錄 一邊學吧。
傳統定義:A block is an anonymous inline collection of code, and sometimes also called a "closure".就是活:block其實一個匿名的內聯代碼集合體 ,有時也稱為“閉包”。
Block從IOS4.0+ 和 Mac OS X 10.6+引進的對C語言的擴展,用來實現匿名函數的特性
這好像是重點:一個函數裡定義了個block,這個block可以訪問該函數的內部變量。
看一個簡單的block代碼示例:上代碼
- (void) block{
int (^Multiply)(int,int) = ^(int num1, int num2)
{
return num1 * num2;
};
NSLog(@"multiply :%d",Multiply(2,3));
__block int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
multiplier ++;//假如上面的 multiplier 變量沒有用 __block 定義的話,編譯報錯;
return num * multiplier;
};
NSLog(@"myBlock :%d",myBlock(2));
}
從上面可以看出:Block除了能夠定義參數列表、返回類型外,還能夠獲取被定義時的詞法范圍內的狀態(比如局部變量),並且在一定條件下(比如使用__block變量)能夠修改這些狀態。此外,這些可修改的狀態在相同詞法范圍內的多個block之間是共享的,即便出了該詞法范圍(比如棧展開,出了作用域),仍可以繼續共享或者修改這些狀態。
通常來說,block都是一些簡短代碼片段的封裝,適用作工作單元,通常用來做並發任務、遍歷、以及回調。
比如我們可以在遍歷NSArray時做一些事情:直接上代碼
- (void)arrayBlock{
NSMutableArray *mArray = [NSMutableArrayarrayWithObjects:@"a",@"b",@"abc",nil];
NSMutableArray *mArrayCount = [NSMutableArrayarrayWithCapacity:1];
[mArray enumerateObjectsWithOptions:NSEnumerationConcurrentusingBlock: ^(id obj,NSUInteger idx,BOOL *stop){
[mArrayCount addObject:[NSNumbernumberWithInt:[obj length]]];
}];
NSLog(@"%@",mArrayCount);
}
打印結果:
2013-08-07 11:24:45.980 Block[1744:c07] (
1,
1,
3
)
而在很多框架中,block越來越經常被用作回調函數,取代傳統的回調方式,具體看下面幾點:
用block作為回調函數,可以使得程序員在寫代碼更順暢,不用中途跑到另一個地方寫一個回調函數,有時還要考慮這個回調函數放在哪裡比較合適。采用block,可以在調用函數時直接寫後續處理代碼,將其作為參數傳遞過去,供其任務執行結束時回調。
另一個好處,就是采用block作為回調,可以直接訪問局部變量。比如我要在一批用戶中修改一個用戶的name,修改完成後通過回調更新對應用戶的單元格UI。這時候我需要知道對應用戶單元格的index,如果采用傳統回調方式,要麼需要將index帶過去,回調時再回傳過來;要麼通過外部作用域記錄當前操作單元格的index(這限制了一次只能修改一個用戶的name);要麼遍歷找到對應用戶。而使用block,則可以直接訪問單元格的index。
這份文檔中提到block的幾種適用場合
http://developer.apple.com/library/ios/#featuredarticles/Short_Practical_Guide_Blocks/index.html
任務完成時回調處理
消息監聽回調處理
錯誤回調處理
枚舉回調
視圖動畫、變換
排序
Block功能:
和函數一樣擁有參數類型
有推斷和聲明的返回類型
可以捕獲它的聲明所在相同作用域的狀態,可以和其他定義在相同作用域范圍的block進行共享修改
你可以拷貝一個 block,甚至可以把它作為可執行路徑傳遞給其他線程(或者在自己的線程內傳遞給run loop)。編譯器和運行時會在整個block生命周期裡面為所有block 引用變量保留一個副本。盡管 blocks 在純 C 和 C++上面可用,但是一個 block也同樣是一個 Objective-C 的對象。
Block的用處:
Blocks通常代表一個很小、自包的代碼片段。因此它們作為封裝的工作單元在並發執行,或在一個集合項上,或當其他操作完成時的回調的時候非常實用。
Blocks 作為傳統回調函數的一個實用的替代辦法,有以下兩個原因:
它們可以讓你在調用的地方編寫代碼實現後面將要執行的操作。
因此 Blocks 通常作為框架方法的參數。
它們允許你訪問局部變量。
而不是需要使用一個你想要執行操作時集成所有上下文的信息的數據結構來
進行回調,你可以直接簡單的訪問局部變量。
還有一點:
Blocks 還支持可變參數(...)。一個沒有使用任何參數的 block 必須在參數列表上面用 void 標明。
為了可以在 block 內修改一個變量,你需要使用__block 存儲類型修飾符來標識該變量。
看下面幾種聲明:
void(^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntAndCharWithVoidArguments)(int,char);
void(^arrayOfTenBlockReturningVoidWithIntArgument[10])(int);
__block存儲類型
作為一種優化,block存儲在棧上面,就像blocks本身一樣。如果使用Block_copy拷貝了block 的一個副本(或者在Objective-C 裡面給block 發送了一條copy 消息),變量會被拷貝到堆上面。所以一個__block變量的地址可以隨時間推移而被更改。
使用__block 的變量有兩個限制:它們不能是可變長的數組,並且它們不能是包含有 C99 可變長度的數組變量的數據結構。
使用block作為方法函數的參數,上代碼:
NSArray *array = [NSArrayarrayWithObjects: @"A",@"B", @"C", @"A",@"B", @"Z",@"G",@"are", @"Q", nil];
NSSet *filterSet = [NSSet setWithObjects: @"A",@"Z", @"Q", nil];
BOOL (^text)(idobj,NSUInteger idx,BOOL *stop);
text = ^(id obj, NSUInteger idx, BOOL *stop) {
if (idx < 5) {
if ([filterSet containsObject: obj]) {
return YES;
}
}
return NO;
};
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
NSLog(@"indexes: %@", indexes);
該 block 是內聯定義的 ,繼續上代碼:
__block BOOL found =NO;
NSSet *aSet = [NSSetsetWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string =@"gamma";
[aSet enumerateObjectsUsingBlock:^(id obj,BOOL *stop){
if ([objlocalizedCaseInsensitiveCompare:string] == NSOrderedSame)
{
*stop = YES;
found = YES;
}
}];