? ? ? ? ?用block可以定義任意的代碼片段,將其像對象一樣傳入另一個方法;它是c級別的語法,和C語言中的函數(shù)指針非常相似。在C語言中,函數(shù)指針和block寫法分別如下
int (* func1)(int a, int b);//c函數(shù)指針
返回類型 | 函數(shù)名 | 參數(shù)
int (^backValue)(int num);//block
返回類型 | 方法名? |? 參數(shù)
? ? ? ?從表面來看兩者唯一的區(qū)別就在于*和^。通常來說,block都是一些簡短代碼片段的封裝,適用做工作單元,通常用來做并發(fā)任務(wù)、遍歷以及回調(diào)。
block對象也是對象,實例化方法如下
? ? ? ? ?int (^backValue)(int) = ^(int num) {
? ? ? ? ? ? ?return num+1;
? ? ? ? };
? ? ? ? block需要注意的一個特性就是"Variable Capturing",直譯過來就是捕捉變量。block會將“捕捉”到的變量復(fù)制一份,然后對復(fù)制品進(jìn)行操作,這是非常重要的一點。對于以下代碼來說打印結(jié)果是b-->2,這是因為在block作用域內(nèi)會復(fù)制a,然后對復(fù)制的a進(jìn)行操作,作用域外的a就不會產(chǎn)生變化,所以,在a++;之后再調(diào)用backValue(1),結(jié)果是2
? ? ? ?int a = 1;
? ? ? int (^backValue)(int) = ^(int num) {
? ? ? return num+a;
? ? ?};
? ? ?a += 1;
? ? int b = backValue(1);
? ? NSLog(@"b-->%d", b);
如果希望block作用域內(nèi)可以修改外邊的變量,可以使用__block(注意是兩個下劃線)來修飾int a。這樣以來結(jié)果就會是b-->3。
示例:

輸出結(jié)果:(三次輸出的內(nèi)存地址是完全相同的,注意代碼執(zhí)行順序)
? ? ?0x7fff5c5e0bc0
? ? 100--0x7fff5c5e0bc0
? ? ?aa = 100--0x7fff5c5e0bc0
? ? ? ? 結(jié)論:當(dāng)用__block來修飾變量int aa = 10 的時候,在block內(nèi)部調(diào)用變量aa的時候,使用的是在相同內(nèi)存空間中修改過的變量aa的值,即變量aa的值從10修改為了100。那么此時打印aa得到的結(jié)果就是修改過的值100,且地址還是開始聲明aa的時候所開辟的內(nèi)存空間的地址

輸出結(jié)果:
? ? 0x7fff58b61bc8
?100--0x7fff58b61bc8
? aa = 10--0x7fff58b61bb8
結(jié)論:對比上面使用了__block修飾變量的輸出結(jié)果可以看到,當(dāng)未使用__block來修飾變量aa的時候,在block內(nèi)部使用的變量aa的內(nèi)存地址和聲明的 ?int aa = 10開辟的內(nèi)存空間的地址是不同的。重點到了,那么是什么原因?qū)е碌倪@個問題呢????
? ? ? 原因是:當(dāng)變量aa未使用__block修飾的時候,block會將“捕捉”到的變量復(fù)制一份,然后對復(fù)制品進(jìn)行操作。也就是說預(yù)編譯的時候,block已經(jīng)把變量aa復(fù)制了一份出來(可以理解為重新聲明了一個同名的變量,此時的兩個變量在內(nèi)存中的地址是不同的),當(dāng)最后一句代碼執(zhí)行了block調(diào)用的時候,block內(nèi)部使用的是自己復(fù)制(本質(zhì)上是深拷貝)出來的一個變量
剛才的例子中a是一個基本類型的變量,如果block外是一個oc對象的話,結(jié)果就又不一樣了,比如下邊代碼,結(jié)果就是"block作用域內(nèi)賦值"
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 300, 300, 40)];
label.text = @"block作用域外賦值";
void (^backValue)(NSString *) = ^(NSString *str) {
label.text = str;
};
backValue(@"block作用域內(nèi)賦值");
[self.view addSubview:label];
這是因為UILabel *label;定義了一個對象指針,在block作用域內(nèi)復(fù)制的是label這個指針,而不是對象,復(fù)制之后的指針仍然指向原來的對象,所以對label.text進(jìn)行操作是可以修改原對象的,但是作用域內(nèi)如果想下邊這樣寫就不行了,會得到提示Variable is not assignable。
label = [[UILabel alloc] init];