Objective-C Block與函式指標比較、分析
阿新 • • 發佈:2019-02-15
本來不應該將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;
}
以上程式碼分別對 無引數無返回值 、無引數返回int、int引數返回int 三種情況進行了示例。需要說明以下幾點:
- 對函式指標進行賦值的時候,直接使用預先定義好的函式的名稱,或者在函式名稱前面加上一個取地址符號**&**都是一樣的,C裡面函式名稱本身就可以表示函式的地址。
- Objective-C裡面Block和函式指標宣告形式上頗為相像,可看成是*號換成了**^**這個塊特有的符號。
- 定義block的時候,如果引數和返回值為void可以簡寫:
void (^voidVoidBlock)() = ^{
NSLog(@"In block voidVoidBlock...");
};
voidVoidBlock();
- 此外還可以通過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和函式指標的異同
相似點
- 函式指標和Block都可以實現回撥的操作,宣告上也很相似,實現上都可以看成是一個程式碼片段。
- 函式指標型別和Block型別都可以作為變數和函式引數的型別。(typedef定義別名之後,這個別名就是一個型別)
不同點
- 函式指標只能指向預先定義好的函式程式碼塊(可以是其他檔案裡面定義,通過函式引數動態傳入的),函式地址是在編譯連結時就已經確定好的。
- Block本質是Objective-C物件,是NSObject的子類,可以接收訊息。
- 函式裡面只能訪問全域性變數,而Block程式碼塊不光能訪問全域性變數,還擁有當前棧記憶體和堆記憶體變數的可讀性(當然通過__block訪問指示符修飾的區域性變數還可以在block程式碼塊裡面進行修改)。
- 從記憶體的角度看,函式指標只不過是指向程式碼區的一段可執行程式碼,而block實際上是程式執行過程中在棧記憶體動態建立的物件,可以向其傳送copy訊息將block物件拷貝到堆記憶體,以延長其生命週期。
- 關於第2點可以作一個實驗,在定義block之後打一個斷點,Cmd+R執行後,可以在除錯視窗看到,block確實是一個物件,擁有isa指標。
- 另外,採用block寫法,gcc編譯出來可執行檔案體積更大,這應該還是跟block是物件有關。