1、block 介紹閉包
block 是c語言層次的語句,c中的方法比較類似.在一些其餘的語言中,block 有時也被稱爲"closure"(閉包). 她能夠被聲明爲指針變量,做爲參數傳遞以供回調,在異步調用上也很是方便;app
block 是一種匿名內聯的代碼集合,文檔上羅列了她的一些功能:dom
一、有如方法同樣的參數列表異步
二、有返回類型async
三、能夠在其聲明時所在的做用域中佔有狀態oop
四、能夠在其做用域中選擇性的更改狀態ui
五、能夠與相同做用域中的其餘代碼塊分享變更的可能性spa
六、儘管其(棧)做用域被銷燬,她能夠繼續分享和更改在該做用域中定義的狀態線程
block 在建立的時候是被存儲於棧中,相似於方法,可是存在於棧中將面臨其做用域被銷燬的可能 ,因此block能夠被複制,也能夠被傳遞至其餘線程去執行,複製的block將存在於堆中,如此就算原來對應的棧空間被銷戶,堆中的block仍然發揮做用,這些對於使用上來講是隱形的,開發者能夠沒必要要手動發送Block_copy()消息進行復制,運行時會自動那麼作,同時他還會管理block中的各類性質的變量,這個下面會介紹。指針
2、用法
一、聲明
block的聲明相似於方法指針,只是將*換成了^
形式: 返回類型 (^變量名)(參數列表)
下面是文檔聲明block變量的例子:
void (^blockReturningVoidWithVoidArgument)(void); |
int (^blockReturningIntWithIntAndCharArguments)(int, char); |
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); |
固然 ,typedef 用起來更簡便
typedef float (^MyBlockType)(float, float); |
MyBlockType myFirstBlock = // ... ; |
MyBlockType mySecondBlock = // ... ; |
形式:
^(參數列表){
//body
};
若參數爲void 則,(void)能夠省略,文檔例子:
float (^oneFrom)(float); |
oneFrom = ^(float aFloat) { |
float result = aFloat - 1.0; |
return result; |
}; |
三、全局block
使用中,咱們常常建立全局block:
#import <stdio.h> |
int GlobalInt = 0; |
int (^getGlobalInt)(void) = ^{ return GlobalInt; }; |
3、block中的變量
代碼塊中的使用變量,分別是:全局變量(如static修飾的變量),全局方法(嚴格來講不能教變量),本地變量和參數,__block修飾的變量,靜態引入。
文檔中羅列了在block 中使用變量的規則:
一、可訪問全局變量,包括存在於block做用域中的static 變量
二、可訪問block的參數
三、棧變量(非靜態,即本體存在於棧中,如int),在程序執行到定義該block的時候將會捕捉該變量的值並當作const變量來使用,至關於複製了一個同名同值的const變量在block中使用。
四、block做用域中聲明的本地變量,若是在聲明時使用了__block關鍵字,那麼改變量在block中是可寫的,不然爲可讀。若多個Block使用了該__block類型的同一個變量,更改是共享的。
五、block中聲明的局部變量,就像方法中聲明的局部變量,隨意使用
文檔中給出了block使用棧變量的兩個例子:
1)
int x = 123; |
void (^printXAndY)(int) = ^(int y) { |
printf("%d %d\n", x, y); |
}; |
x=789; //這句是另外加的,可作參考 |
printXAndY(456); // prints: 123 456 |
2)
int x = 123; |
void (^printXAndY)(int) = ^(int y) { |
x = x + y; // error |
printf("%d %d\n", x, y); |
}; |
還有__block狀況的例子:
1)
__block int x = 123; // x lives in block storage |
void (^printXAndY)(int) = ^(int y) { |
x = x + y; |
printf("%d %d\n", x, y); |
}; |
printXAndY(456); // prints: 579 456 |
extern NSInteger CounterGlobal; |
static NSInteger CounterStatic; |
{ |
NSInteger localCounter = 42; |
__block char localCharacter; |
void (^aBlock)(void) = ^(void) { |
++CounterGlobal; |
++CounterStatic; |
CounterGlobal = localCounter; // localCounter fixed at block creation |
localCharacter = 'a'; // sets localCharacter in enclosing scope |
}; |
++localCounter; // unseen by the block |
localCharacter = 'b'; |
aBlock(); // execute the block |
// localCharacter now 'a' |
} |
文檔中還介紹了Object和block 變量的關係。block在執行時使用了object變量:
一、若是是經過引用來訪問實例變量,那麼將會有一個強引用指向self
二、若是是經過值來訪問實例變量,那麼將會有一個強引用指向該變量
下面的例子展現了兩種不一樣的狀況:
dispatch_async(queue, ^{ |
// instanceVariable is used by reference, a strong reference is made to self |
doSomethingWithObject(instanceVariable); |
}); |
id localVariable = instanceVariable; |
dispatch_async(queue, ^{ |
/* |
localVariable is used by value, a strong reference is made to localVariable |
(and not to self). |
*/ |
doSomethingWithObject(localVariable); |
}); |
4、block的使用
若是你像聲明變量那樣聲明block ,你能夠像使用方法同樣使用她,文檔給出了兩個例子:
int (^oneFrom)(int) = ^(int anInt) { |
return anInt - 1; |
}; |
printf("1 from 10 is %d", oneFrom(10)); |
// Prints "1 from 10 is 9" |
float (^distanceTraveled)(float, float, float) = |
^(float startingSpeed, float acceleration, float time) { |
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time); |
return distance; |
}; |
float howFar = distanceTraveled(0.0, 9.8, 1.0); |
// howFar = 4.9 |
咱們能夠將block做爲方法的參數進行傳遞,在不少狀況下,須要block參數的地方,block不須要聲明只須要簡單的進行內聯實現就能夠了,這個內聯實現就像不少其餘語言中的匿名類或者匿名方法同樣,在建立的同時就直接使用了。文檔的例子以下:
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" }; |
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) { |
char *left = *(char **)l; |
char *right = *(char **)r; |
return strncmp(left, right, 1); |
}); |
// Block implementation ends at "}" |
// myCharacters is now { "Charles Condomine", "George", "TomJohn" } |
5、注意點
目前我總結使用代碼塊的注意點:
一、避免循環引用,若是你在self中定義了block變量,並在self中實現該block的時候,使用了相似self.變量的語句(_變量 也至關於 self.變量),將會形成循環引用。這時請使用__weak 來修飾self;
二、避免懸指針狀況,由於block開始的時候是棧存儲的,在被copy到堆中前,其可能形成實質做用域和變量做用域不一樣而致使變量成爲懸指針的狀況,文檔給出了兩個例子:
void dontDoThis() { |
void (^blockArray[3])(void); // an array of 3 block references |
for (int i = 0; i < 3; ++i) { |
blockArray[i] = ^{ printf("hello, %d\n", i); }; |
// WRONG: The block literal scope is the "for" loop. |
} |
} |
void dontDoThisEither() { |
void (^block)(void); |
int i = random(): |
if (i > 1000) { |
block = ^{ printf("got i at: %d\n", i); }; |
// WRONG: The block literal scope is the "then" clause. |
} |
// ... |
} |
觀看的朋友如發現有誤,請指出。謝謝