1. 程式人生 > >Objective-C Block與函式指標比較、分析

Objective-C Block與函式指標比較、分析


本來不應該將OC block與函式指標進行比較的,這兩者除了宣告形式上類似,都可用來實現回撥(CallBack)之外,其不同的地方會更多。

今天從一個小例子開始對Objective-C裡面函式指標和Block進行剖析。
函式指標是C語言裡面就有的,而Objective-C是C的超集,對C語言作了很多擴充套件(這種擴充套件主要是依賴編譯器和執行時系統來完成的)。從表層來看,OC對C的擴充套件主要就是@符號的引入,包括定義類、協議、屬性、裝箱、@synchronized、丟擲異常等等都有@符號的身影。

如果你也是做過C/C++開發,再來接觸OC裡面Block的話,應該也會有一種特別的親切感。

基本用法

#import <Foundation/Foundation.h>
#import <stdlib.h>

//定義無引數、無返回值的函式
void voidVoidTest() {
    NSLog(@"In function voidVoidTest()...");
}
//無引數返回int
int intVoidTest() {
    NSLog(@"In function intVoidTest()...");
    return arc4random()%10;
}
//帶一個int型別引數、返回int
int intIntTest(int val) {
    NSLog
(@"In function intIntTest()...val=%i", val); return val<<1; } int main(int argc, const char * argv[]) { @autoreleasepool { //函式指標 void (*funcVoidVoid)() = &voidVoidTest; //呼叫所指向的函式 funcVoidVoid(); int (*funcIntVoid)() = intVoidTest; NSLog
(@"funcIntVoid called: %i", funcIntVoid()); int (*funcIntInt)(int) = intIntTest; NSLog(@"funcIntInt called: %i", funcIntInt(20)); //Block void (^voidVoidBlock)() = ^void (void){ NSLog(@"In block voidVoidBlock..."); }; //呼叫block voidVoidBlock(); int (^intVoidBlock)() = ^int (void) { NSLog(@"In block intVoidBlock..."); return arc4random()%10; }; NSLog(@"intVoidBlock called: %i", intVoidBlock()); int (^intIntBlcok)(int) = ^int (int val) { NSLog(@"In block intIntBlcok..."); return val<<1; }; NSLog(@"intIntBlcok called: %i", intIntBlcok(100)); } return 0; }

以上程式碼分別對 無引數無返回值無引數返回intint引數返回int 三種情況進行了示例。需要說明以下幾點:

  1. 對函式指標進行賦值的時候,直接使用預先定義好的函式的名稱,或者在函式名稱前面加上一個取地址符號**&**都是一樣的,C裡面函式名稱本身就可以表示函式的地址。
  2. Objective-C裡面Block和函式指標宣告形式上頗為相像,可看成是*號換成了**^**這個塊特有的符號。
  3. 定義block的時候,如果引數和返回值為void可以簡寫:
void (^voidVoidBlock)() = ^{
     NSLog(@"In block voidVoidBlock...");
 };
 voidVoidBlock();
  1. 此外還可以通過typedef給函式指標和Block定義一個別名,例如:
typedef int (*FuncIntInt)(int);
typedef int (^IntIntBlock)(int);

//通過typedef定義型別
FuncIntInt func = intIntTest;
NSLog(@"typedef func: %i", func(1000));
IntIntBlock block = ^int (int val) {
    NSLog(@"In typedef block...val=%i", val);
    return val<<1;
};
NSLog(@"typedef block: %i", block(134));

Block和函式指標的異同

相似點

  1. 函式指標和Block都可以實現回撥的操作,宣告上也很相似,實現上都可以看成是一個程式碼片段。
  2. 函式指標型別和Block型別都可以作為變數和函式引數的型別。(typedef定義別名之後,這個別名就是一個型別)

不同點

  1. 函式指標只能指向預先定義好的函式程式碼塊(可以是其他檔案裡面定義,通過函式引數動態傳入的),函式地址是在編譯連結時就已經確定好的。
  2. Block本質是Objective-C物件,是NSObject的子類,可以接收訊息。
  3. 函式裡面只能訪問全域性變數,而Block程式碼塊不光能訪問全域性變數,還擁有當前棧記憶體和堆記憶體變數的可讀性(當然通過__block訪問指示符修飾的區域性變數還可以在block程式碼塊裡面進行修改)。
  4. 從記憶體的角度看,函式指標只不過是指向程式碼區的一段可執行程式碼,而block實際上是程式執行過程中在棧記憶體動態建立的物件,可以向其傳送copy訊息將block物件拷貝到堆記憶體,以延長其生命週期。
  • 關於第2點可以作一個實驗,在定義block之後打一個斷點,Cmd+R執行後,可以在除錯視窗看到,block確實是一個物件,擁有isa指標。
  • 另外,採用block寫法,gcc編譯出來可執行檔案體積更大,這應該還是跟block是物件有關。