Blocks是C語言的擴種功能,是帶有自動變量(局部變量)的匿名函數。數組
^void (int event){
printf("buttonId: %d event = %d\n", i , event);
}
複製代碼
與通常函數相比,有兩點不一樣bash
如下爲Block語法ide
^
返回值類型
參數列表
表達式
函數
如:ui
^int (int count){return count+1;}
複製代碼
能夠省略返回值類型spa
^
參數列表
表達式
設計
省略返回值類型時,若是表達式中有return語句就使用該返回值的類型,若是表達式中沒有人return語句就使用void類型。表達式中含有多個return語句時,全部return的返回值類型必須相同。指針
如:code
^(int count){ return count+1;}
複製代碼
若是不是用參數,參數列表也能夠省略。cdn
^
表達式
如:
^void (void){ printf("Blocks\n");}
複製代碼
可省略爲:
^{ printf("Blocks\n");}
複製代碼
在定義C語言函數時,就能夠將所定義函數的地址賦值給函數指針類型的變量中。
int func(int count)
{
return count+1;
}
int (*funcptr) (int) = &func;
複製代碼
這樣一來,函數func的地址就能賦值給函數指針類型變量funcptr中了。
注:引用知乎fan wang關於指針的回答,解釋一下指針類型變量原理
做者:fan wang 連接:https://www.zhihu.com/question/31022750/answer/50629732 來源:知乎 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
一圖勝千言1. 聲明變量:C語言聲明一個變量時,編譯器在內存中留出一個惟一的地址單元來存儲變量,以下圖,變量var初始化爲100,編譯器將地址爲1004的內存單元留給變量,並將地址1004和該變量的名稱關聯起來。
2.建立指針:變量var的地址是1004,是一個數字,地址的這個數字能夠用另外一個變量來保存它,假設這個變量爲p,此時變量p未被初始化,系統爲它分配了空間,但值還不肯定,以下圖所示。 3.初始化指針,將變量var的地址存儲到變量p中,初始化後(p=&var),p指向var,稱爲一個指向var的指針。指針是一個變量,它存儲了另外一個變量的地址。 4.聲明指針:typename *p 其中typename指的是var的變量類型,能夠是 short ,char ,float,由於每一個類型佔用的內存字節不一樣,short佔2個字節,char佔1個字節,float佔4個字節,指針的值等於它指向變量的第一個字節的地址 。*是間接運算符,說明p是指針變量,與非指針變量區別開來。 5.*p和var指的是var的內容;p和&var指的是var的地址 6.既然指針*p的值等於var,p的值等於&var,爲何要多發明這一個指針符號增長記憶量呢。指針主要的功能有兩個:避免副本和共享數據。指針的重要功能是函數之間傳遞參數。 talk is cheap, show me the code! 假設用c語言設計一個遊戲,控制人物向前走的函數爲 go_forward(),這個函數接收遊戲人物的座標(int x,int y) 兩個變量,對這兩個變量進行加減操做。
#include <stdio.h>
void go_forward(int position_x,int position_y)
{
position_x=position_x+1;
position_y=position_y+1;
}
int main()
{
int x=0;
int y=0;
go_forward(x,y);
printf("當前座標爲:%d,%d \n",x,y);
return 0;
}
複製代碼
你但願執行go_forward()函數後x,y座標都+1,輸出爲(1,1),可是結果仍是(0,0)緣由爲C語言調用函數的方式是按值傳遞參數,以x參數爲例,剛開始main函數中有一個x的局部變量,值爲0,當計算機調用go_forward()函數時,它將變量x的值複製給了參數position_x,這只是一個賦值過程將變量x賦值給變量position_x,至關於 position_x=x 命令,而這個命令,x的值是不發生變化的,結果以下圖所示,x的值仍爲0,position_x的值變爲1。
解決方法,傳遞指針,用指針告訴go_forward()函數參數x的值的地址,go_forward()函數就能修改對應地址中的內容。因此用指針的主要緣由是讓函數共享存儲器,一個函數能夠修改另外一個函數建立的數據,只要提供數據在內存中的地址,修改代碼以下。
#include <stdio.h>
void go_foward(int *position_x,int*position_y)
{
*position_x=*position_x+1;
*position_y=*position_y+1;
}
int main()
{
int x=0;
int y=0;
go_forward(&x,&y);
printf("當前座標爲:%d,%d \n",x,y);
return 0;
}
複製代碼
運行結果:當前座標爲:1,1
聲明Block類型變量的示例以下:
int (^blk)(int);
複製代碼
與前面的使用函數指針的源代碼對比可知,聲明Block類型變量僅僅是將聲明函數指針類型變量的"*"變爲"^"。該Block類型變量與通常C語言變量徹底相同,可用做:
int (^blk)(int) = ^(int count){
return count+1;
}
複製代碼
由"^"開始的Block語法生成的Block被賦值給變量blk中。由於與一般的變量相同,因此固然也能夠由Block類型變量向Block類型變量賦值。
int (^bkl1)(int) = blk;
int (^blk2)(int);
blk2 = blk1;
複製代碼
在函數參數中使用Block類型變量能夠向函數傳遞Block。
void func(int (^block)(int)){
}
複製代碼
在函數返回值中指定Block類型,能夠將Block做爲函數的返回值返回。
int (^func()(int)){
return ^(int count){
return count+1;
}
}
複製代碼
由此可知,在函數參數和返回值中使用Block類型變量是,記述方式極爲複雜,因此,使用typedef來解決。
typedef int (^blk_t)(int);
複製代碼
如上所示,經過使用typedef可聲明「blk_t」類型變量。
void func(int (^block)(int)){
}
複製代碼
能夠變爲:
void func(blk_t blk){
}
複製代碼
int (^func()(int)){
return ^(int count){
return count+1;
}
}
複製代碼
能夠變爲:
blk_t func(){
return ^(int count){
return count+1;
}
}
複製代碼
另外,將賦值給Block類型變量中的Block方法像C語言一般的函數調用那樣使用,這種方法與使用函數指針類型變量調用函數的方法幾乎徹底相同。 例:變量funcptr爲函數指針類型是,想下面這樣調用函數指針類型變量。
int result = (*funcptr) (10);
複製代碼
變量blk爲Block 類型的狀況下,這樣調用Block類型變量:
int result = blk(10);
複製代碼
在函數參數中使用Block類型變量並在函數中執行Block的例子以下:
int func(blk_t blk, int rate){
return blk(rate);
}
複製代碼
在OC中也能夠:
- (int) methodUsingBlock:(blk_t) blk rate:(int)rate{
return blk(rate);
}
複製代碼
Block類型變量可徹底像C語言變量同樣使用,所以也可使用指向Block類型變量的指針,即Block的指針類型變量。
typedef int(^blk_t)(int);
blk_t blk = ^(int count){ return count+1;};
blk_t *blkptr = &blk;
(*blkptr)(10);
複製代碼
int main(){
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void)=^{
printf(fmt,val);
};
val = 2;
fmt = "These values were changed.val=%d\n";
blk();
return 0;
}
複製代碼
運行結果爲:
val = 10
複製代碼
在該源代碼中,Block語法的表達式使用的是它以前聲明的自動變量fmt和val。Block中,Block表達式截獲所使用的自動變量的值,即保存該自動變量的瞬間值。由於Block表達式保存了自動變量的值,因此在執行Block語法後,即便改寫Block中使用的自動變量的值也不會影響Block執行時自動變量的值。
因爲Block表達式截獲了所使用的自動變量的值,若是在Block中嘗試修改自動變量的值會編譯錯誤:
int val = 0;
void (^blk)(void) = ^{ val = 1; };
blk();
printf("val = %d\n",val);
複製代碼
編譯以後:
Variable is not assignable (missing __block type specifier)
複製代碼
若想在Block語法的表達式中將值賦給在Block語法外聲明的自動變量,須要在該自動變量上附加__block說明符。
上述代碼修改成:
__block int val = 0;
void (^blk)(void) = ^{ val = 1; };
blk();
printf("val = %d\n",val);
複製代碼
運行結果爲:
val = 1
複製代碼
使用附有__block說明符的自動變量可在Block中賦值,該變量成爲__block變量。
若是截獲Objective-C對象,調用變動該對象的方法以下:
id array = [[NSMutableArray alloc] init];
void (^block)(void)=^{
id obj = [[NSObject alloc] init];
[array addObject : obj];
};
複製代碼
編譯運行後發現沒有問題,可是向截獲的變量array賦值則會產生編譯錯誤。以上代碼中截獲的變量值爲NSMutableArray類的對象。若是用C語言來描述,便是截獲NSMutableArray類對象用的結構體實例指針。雖然賦值給截獲的自動變量array的操做會產生編譯錯誤,但使用截獲的值缺不會有任何問題。
另外,在使用C語言數組時必須當心使用其指針。
const char text[] = "hello";
void (^blk)(void) = ^{
printf("%c\n",text[2]);
};
複製代碼
編譯後報錯:
Cannot refer to declaration with an array type inside block
複製代碼
這是由於在如今的Blocks中,截獲自動變量的方法並無實現對C語言數組的截獲,這時候,可使用指針解決該問題。
const char *text = "hello";
void (^blk)(void) = ^{
printf("%c\n",text[2]);
};
複製代碼