由《Objective-C高級編程》第二章入手:
1.block是C語言的一項重要的特性,block到底是什么?
簡而言之奶稠,block是帶有自動變量的匿名函數(shù),匿名函數(shù):即不帶名稱的函數(shù)
補一下c語言的一些:
//聲明 調(diào)用函數(shù)
int func(int count);
int result = func(10);
下面這個使用函數(shù)指針來代替調(diào)用函數(shù),似乎不用知道函數(shù)名也能直接使用該函數(shù)
int result = (*funcptr)(10);
但實際上 如果不使用想賦值的函數(shù)的名稱谜诫,就無法取得該函數(shù)的地址
int (*funcptr)(int) = &func;
int result = (*funcptr)(10);
而通過block可以使用不帶名稱的函數(shù),能夠編寫不帶名稱的函數(shù)對程序員來說相當(dāng)具有吸引力涮阔。
2.Block語法與Block類型變量猜绣,可以理解“帶有自動變量值的匿名函數(shù)”中“匿名函數(shù)”。而“帶有自動變量值”在Blocks中表現(xiàn)為“截獲自動變量值”敬特。而自動變量(局部變量)的特點:
- 函數(shù)內(nèi)部聲明
- 僅當(dāng)函數(shù)執(zhí)行時存在
- 僅在本文件本函數(shù)內(nèi)可訪問
- 存儲位置:自動保存在函數(shù)的每次執(zhí)行的【棧幀】中掰邢,并隨著函數(shù)結(jié)束后自動釋放,另外伟阔,函數(shù)每次執(zhí)行則保存在【椑敝】中
- (float)caculateResult{
float a = 1.0;
float b = 2.0;
return a + b;
}
3.block與函數(shù)的區(qū)別:
block是封裝了一段代碼的OC對象,可以被設(shè)為Property, 在調(diào)用block的地方block都會被替換成相應(yīng)的代碼皱炉,相當(dāng)于內(nèi)聯(lián)函數(shù)怀估。
函數(shù)可以使代碼更加整潔易讀,使用block會使代碼可讀性變差合搅,另外函數(shù)可以做單元測試多搀,block無法做單元測試。
4.block語法
block可以認為是匿名的C函數(shù)灾部,它的語法格式是這樣的:
^ (int i){
return 0;
}
實際上康铭,該block語法使用率省略方式,其完整形式如下:
^ void(int i){
return 0;
}
如上赌髓,所以與C語言函數(shù)相比 有兩點不同:
1.沒有函數(shù)名
2.帶有 ^
即bloc格式為: ^ 返回值類型 (參數(shù)列表) { C語言中允許使用的表達式 }
還有一種省略返回值類型的格式:
^ 參數(shù)列表 表達式
^ (int count) {
return count + 1;
}
不使用參數(shù)時 參數(shù)列表也可以省略:
^ void (void) {
printf("blocks\n");
}
可省略為:
^ {
printf("blocks\n");
}
5.源代碼中一旦使用了block語法就相當(dāng)于生成了可賦值給block類型變量的值
block既指源代碼中的block語法从藤,也指由block語法所生成的值
下面是用語法將block值賦值給block類型變量
int (^blk)(int) = ^(int count) {
return count + 1;
}
在函數(shù)返回值中指定block類型 可以將block作為函數(shù)的返回值返回
int (^func())(int)
{
return ^(int count){ return (count+1) };
}
C中 在函數(shù)參數(shù)中使用block類型變量并在函數(shù)中執(zhí)行block的例子如下:
int func(blk_t blk, int rate)
{
return blk(rate);
}
OC中:
(int) methodBlock:(blk_t)blk rate:(int)rate
{
return blk(rate);
}
typedef聲明block類型變量的格式:
typedef 返回值類型 (^塊名) (參數(shù)列表)
6.block作用: 截獲自動變量值:
block表達式可以截獲所使用的自動(局部)變量的值,即保存該自動變量的瞬間值锁蠕,即使block的調(diào)用語句在該值修改之后被調(diào)用夷野,但是實現(xiàn)部分在該值修改之前即可。
typedef void (^block_t)();
int main ()
{
int a = 0;
int b = 1;
block_t blk = ^ () {
printf("block: %d", a);
}
a = 2;
blk();
return 0;
}
7.block中如果給截獲的自動變量賦值 就會產(chǎn)生編譯錯誤荣倾。
但是調(diào)用變更該截獲的obje-C對象的方法不會產(chǎn)生編譯錯誤:
id array = [[NSMutalbeArray alloc] init];
void (^blk)(void) = ^ {
id obj = [[NSObject alloc] init];
[array addObject:obj];
};
8.block中截獲自動變量的方法并沒有實現(xiàn)對C語言數(shù)組的截獲悯搔,這樣打印C語言數(shù)組會出現(xiàn)編譯錯誤,
但是使用數(shù)組指針可以解決該問題
9.總的來說舌仍,截獲自動變量值意味著在執(zhí)行block語法時鳖孤,block語法表達式所使用的自動變量值被保存到block的結(jié)構(gòu)體實例(即block自身)中
10.block類共有三種:
- _NSConcreteStackBlock
儲存在程序區(qū)域的數(shù)據(jù)區(qū)域
- _NSConcreteGlobalBlock
儲存在程序區(qū)域的堆區(qū)
- _NSConcreteMallocBlock
儲存在程序區(qū)域的棧區(qū)
11.即使在函數(shù)內(nèi)而不再記述廣域變量的地方使用Block語法時,只要Block不截獲自動變量抡笼,就是block里面的代碼與自動(局部)變量無關(guān),就可以將Block用結(jié)構(gòu)體實例設(shè)置在程序的數(shù)據(jù)區(qū)域
typedef int (^blk_t)(int);
for (int rate = 0; rate < 10; rate++) {
blk_t blk = ^(int count) {
return count;
}
}
總結(jié)就是:
在下面這些情況黄鳍,Block為_NSConcreteGlobalBlock類對象推姻,即配置在程序的數(shù)據(jù)區(qū)域中
- 記述全局變量的地方有Block語法時
- Block語法的表達式中不使用應(yīng)截獲的自動變量時
除此之外的Block語法生成的Block為_NSConcreteStackBlock類對象,且設(shè)置在棧上
12.配置在全局變量上的Block,從變量作用域外也可以通過指針安全的使用框沟,但是
設(shè)置在棧上的Block藏古,如果其所屬變量的作用域結(jié)束增炭,該Block就會被廢棄。
解決方法:
將Block和_block變量從棧上復(fù)制到堆上的方法來解決
復(fù)制到堆上的Block將_NSConcreteMallocBlock類對象寫入Block用結(jié)構(gòu)體實例的成員變量isa
imp1,isa = &__NSConcreteMallocBlock;
13.什么時候棧上的Block復(fù)制到堆拧晕?
- 調(diào)用Block的copy實例方法時
- Block作為函數(shù)返回值返回時
- 將Block賦值給附有_strong修飾符id類型的類或Block類型成員變量時
- 在方法中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時
14.在不調(diào)用copy函數(shù)的情況下隙姿,即使截獲了對象,它也會隨著變量作用域的結(jié)束而廢棄(P125)
因此厂捞,Block中使用對象類型自動變量時输玷,除以下情形外,推薦調(diào)用Block的copy實例方法:
- Block作為函數(shù)返回值返回時
- 將Block賦值給類的附有__strong修飾符的id類型或Block類型成員變量時
- 向方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時
15.即使對象賦值復(fù)制到堆上的附有__strong修飾符的對象類型__block變量中靡馁,只要__block變量在堆上繼續(xù)存在欲鹏,那么該對象就會繼續(xù)處于被持有的狀態(tài)。這與Block中使用賦值給附有__strong修飾符的對象類型自動變量的對象相同
16.這段代碼的執(zhí)行結(jié)果是臭墨?
blk_t blk;
{
id array = [[NSMutableArray alloc] init];
id __weak array2 = array;
blk = [^(id obj) {
[array2 addObject:obj];
NSLog(@"array2 count = %ld", [array2 count]);
} copy];
}
blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);
該源代碼執(zhí)行結(jié)果為:
array2 count = 0;
array2 count = 0;
array2 count = 0;
原因:
- 由于附有__strong修飾符的變量array在該變量作用域結(jié)束的同時被釋放赔嚎,廢棄,nil被賦值在附有__weak修飾符的變量array2中胧弛,該代碼可正常執(zhí)行
17.看一段這個代碼:
- (id)init {
self = [super init];
blk_ = ^{
NSLog(@"obj_ = %@", obj_);
}
return self;
}
會造成編譯錯誤尤误。
因為Block語法內(nèi)使用的obj_實際上截獲了self.對編譯器來說,obj_只不過是對象用結(jié)構(gòu)體的成員變量
blk_ = ^{
NSLog("obj_ = %@", self->obj_);
}
18.只要Block有一次復(fù)制并配置在堆上结缚,就可通過retain實例方法持有
但對于配置在棧上的Block調(diào)用retain實例方法則不起任何作用