浅析OC中的block
一、Block
帶有 自動變量/局部變量 的匿名函數叫做 block ,又叫做 匿名函數 、代碼塊 。
完整形式的 Block 語法與一般的 **C語言 **函數定義相比,僅有兩點不同:
- 沒有函數名 不含函數名是因為其為匿名函數。
- 帶有 “ ^ ” 返回值類型前帶有 ^ ,由于 OSX,iOS 應用程序中將大量使用 Block 因此插入該記號便于查找。
Block語法
- 無參無返
- 有參無返
- 有參有返
- 無參無返
我們使用 “{ }” 限定 Block 的范圍,這一點與函數和方法的定義很相似。
類似于使用一個C函數指針, 我們可以聲明一個變量來存儲 Block :
void (^simpleBlock)(void); //同樣也可以這樣來為block變量賦值 simpleBlock = ^{NSLog(@"This is a block");};需要注意的是: Block 賦值和其它類型的變量賦值一樣, 需要以 ; 結尾。
我們也可以在聲明變量的同時為其賦值:
void (^simpleBlock)(void) = ^{NSLog(@"This is a block");}; //之后我們就可以像這樣調用Block了:simpleBlock();帶有參數和返回值的Block
和函數和方法類似,Block 可以接收參數或者有返回值。
假如我們需要聲明一個兩個 double 類型相乘并返回結果的 Block:
double (^multiplyTwoValues)(double, double); //對應的 block 實現如下^ (double firstValue, double secondValue) {return firstValue * secondValue;} //在這個例子中, 返回值可以通過return 表達式推斷出來. 當然我們也可以像這樣顯式聲明返回值類型:^ double (double firstValue, double secondValue) {return firstValue * secondValue;}在我們聲明并定義了 Block 之后,我們可以就像調用函數一樣,調用 Block 了:
double (^multiplyTwoValues)(double, double) =^(double firstValue, double secondValue) {return firstValue * secondValue;};double result = multiplyTwoValues(2,4);NSLog(@"The result is %f", result);Block捕獲上下文信息
Block 不僅僅包含了可執行的代碼片段,Block 也有能力從臨近作用域捕獲上下文信息。
如果我們定義了一個方法內部的 Block ,Block 可以捕獲方法內作用域的上下文信息:
- (void)testMethod {int anInteger = 42;void (^testBlock)(void) = ^{NSLog(@"Integer is: %i", anInteger);};testBlock(); }在這個例子中,anInteger 在 block 外部聲明,但是在 block 定義時, 它的值被 block “捕獲” 了。
這里捕獲的僅僅是值, 當我們在 block,的定義和 block 的調用之間修改 anInteger 的值時:
int anInteger = 42;void (^testBlock)(void) = ^{NSLog(@"Integer is: %i", anInteger);};anInteger = 84;testBlock();testBlock( ) 的輸出并不會被 anInteger 的新值影響,因為我們只捕獲了值,因此輸出如下:
Integer is: 42這也意味著 Block 不能改變捕獲變量的原值。
__block的使用
如果我們需要從 block 內部改變外部捕獲變量的值時, 我們可以在要修改的外部變量聲明中使用 __block 存儲類型修飾符。使用了 __block 修飾符的變量的儲存空間,被它本身的作用域與引用了它的 __block 的作用域所共享:
__block int anInteger = 42;void (^testBlock)(void) = ^{NSLog(@"Integer is: %i", anInteger);};anInteger = 84;testBlock();由于使用了**__block** 修飾 anInteger ,因此 anInteger 的存儲和 block 的聲明共享。因此輸出如下:
Integer is: 84這也意味著在 Block 中可以修改原值:
__block int anInteger = 42;void (^testBlock)(void) = ^{NSLog(@"Integer is: %i", anInteger);anInteger = 100;};testBlock();NSLog(@"Value of original variable is now: %i", anInteger);此時輸出如下:
Integer is: 42Value of original variable is now: 100函數指針和指針函數
block 本質上是一個函數指針。
指針函數與函數指針表示方法的不同,不要混淆。最簡單的辨別方式就是看函數名前面的指針 * 號有沒有被括號( )包含,如果被包含就是函數指針,反之則是指針函數。
主要的區別是一個是指針變量,一個是函數。
1.指針函數
帶指針的函數,即本質是一個函數。函數返回類型是某一類型的指針。
**類型標識符 函數名(參數表) int f(x,y);
首先它是一個函數,只不過這個函數的返回值是一個地址值。指針函數一定有函數返回值,而且在主調函數中,函數返回值必須賦給同類型的指針變量。
float *fun(); float *p; p = fun( );當一個函數聲明其返回值為一個指針時,實際上就是返回一個地址給調用函數,以用于需要指針或地址的表達式中。由于返回的是一個地址,所以類型說明符一般都是 int。
int *f(int a, int b); //上面的函數聲明又可以寫成如下形式:int* f(int a, int b); //讓指針標志 * 與int緊貼在一起,而與函數名f間隔開,這樣看起來就明了些了,f是函數名,返回值類型是一個int類型的指針。 int *f(int a, int b); // 聲明指針函數 int main(int argc, char* argv[]) {printf("------------------------------ Start\n");int *p1 = NULL;printf("The memeory address of p1 = 0x%x \n", p1);p1 = f(1, 2);printf("The memeory address of p1 = 0x%x \n", p1);printf("*p1 = %d \n", *p1);printf("------------------------------ End\n");getchar();return 0; }/*指針函數的定義,返回值是指針類型int */ int *f(int a, int b) { int *p = (int *)malloc(sizeof(int)); printf("The memeory address of p = 0x%x \n", p); memset(p, 0, sizeof(int)); *p = a + b; printf("*p = %d \n", *p);return p; } ------------------------------ 開始 The memeory address of p1 = 0x0 The memeory address of p = 0x551ed0 *p = 3 The memeory address of p1 = 0x551ed0 *p1 = 3 ------------------------------ 結束通過運行結果,可以看出,指針函數 f 返回的類型是一個指針類型,因為 f 是賦值給 int 類型指針 p1 的,如果不是指針類型,編譯就會出錯。
所以,指針函數就是返回一個地址給調用者,用于需要地址的情況。
2.函數指針(Block)
指向函數(首地址)的指針變量,即本質是一個指針變量。
函數指針說的就是一個指針,但這個指針指向的函數,不是普通的基本數據類型或者類對象。
指向函數的指針包含了函數的地址,可以通過它來調用函數。
*聲明格式:類型說明符 (函數名) (參數)
其實這里不能稱為函數名,應該叫做**指針的變量名。這個特殊的指針指向一個返回整型值的函數**。指針的聲明必須和它指向函數的聲明保持一致。指針名和指針運算符外面的括號改變了默認的運算符優先級。如果沒有圓括號,就變成了一個返回整型指針的函數的原型聲明。
int (*f)(int a, int b); // 聲明函數指針當然,函數指針的返回值也可以是指針。上面的函數指針定義為一個指向一個返回值為整型,有兩個參數并且兩個參數的類型都是整型的函數。
下面是利用函數指針分別求兩個整數的最大值和最小值的用法:
/* 求最大值,返回值是int類型,返回兩個整數中較大的一個*/ int max(int a, int b) { return a > b ? a : b; } /* 求最小值,返回值是int類型,返回兩個整數中較小的一個*/ int min(int a, int b) { return a < b ? a : b; }int(*f)(int, int); // 聲明函數指針,指向返回值類型為int,有兩個參數類型都是int的函數int main(int argc, _TCHAR* argv[]) { printf("------------------------------ Start\n");f = max; // 函數指針f指向求最大值的函數max(將max函數的首地址賦給指針f) int c = (*f)(1, 2);printf("The max value is %d \n", c);f = min; // 函數指針f指向求最小值的函數min(將min函數的首地址賦給指針f) c = (*f)(1, 2);printf("The min value is %d \n", c);printf("------------------------------ End\n"); getchar(); return 0; }/* 求最大值,返回值是int類型,返回兩個整數中較大的一個*/ int max(int a, int b) { return a > b ? a : b; } /* 求最小值,返回值是int類型,返回兩個整數中較小的一個*/ int min(int a, int b) { return a < b ? a : b; }int(*f)(int, int); // 聲明函數指針,指向返回值類型為int,有兩個參數類型都是int的函數int main(int argc, _TCHAR* argv[]) { printf("------------------------------ Start\n");f = max; // 函數指針f指向求最大值的函數max(將max函數的首地址賦給指針f) int c = (*f)(1, 2);printf("The max value is %d \n", c);f = min; // 函數指針f指向求最小值的函數min(將min函數的首地址賦給指針f) c = (*f)(1, 2);printf("The min value is %d \n", c);printf("------------------------------ End\n"); getchar(); return 0; } ------------------------------ Start The max value is 2 The min value is 1 ------------------------------ End例如:
void (*fptr)();把函數的地址賦值給函數指針,可以采用下面兩種形式:
fptr=&Function; fptr=Function;取地址運算符 & 不是必需的,因為單單一個函數標識符就標號表示了它的地址,如果是函數調用,還必須包含一個圓括號括起來的參數表。
可以采用如下兩種方式來通過指針調用函數:
x=(*fptr)(); x=fptr();第二種格式看上去和函數調用無異。但是有些程序員傾向于使用第一種格式,因為它明確指出是通過指針而非函數名來調用函數的。下面舉一個例子:
void (*funcp)(); void FileFunc(), EditFunc(); int main() { funcp = FileFunc; (*funcp)(); funcp = EditFunc; (*funcp)(); } void FileFunc() { printf("FileFunc\n"); }void EditFunc() { printf("EditFunc\n"); }//程序輸出結果為: /*FileFunc EditFunc*/總結
以上是生活随笔為你收集整理的浅析OC中的block的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【金杯至尊】之罗罗的天空----图忆19
- 下一篇: 运筹与决策(三)求解线性规划、运输问题和