上節(jié)講到了Block的實(shí)質(zhì) 挖滤,Blcok轉(zhuǎn)換為Block的結(jié)構(gòu)體類型的自動(dòng)變量页徐,_ block 變量轉(zhuǎn)換為 _block變量的結(jié)構(gòu)體類型的自動(dòng)變量解取。所謂結(jié)構(gòu)體類型的自動(dòng)變量,即棧上生成的該結(jié)構(gòu)體的實(shí)例挺尾。
另外Block也可看作OC的對(duì)象鹅搪。該Block的類_NSConcreteStackBlock,也有很多其他類似的類
_NSConcreteStackBlock
_NSConcreteGlobalBlock
_NSConcreteMallocBlock
通過(guò)名字可以看到Block的存儲(chǔ)域
Block的存儲(chǔ)域
棧區(qū)
堆區(qū)
數(shù)據(jù)存儲(chǔ)區(qū)域(全局區(qū) )
具體如下圖
下面具體分析下三種類型Block
_NSConcreteGlobalBlock
在記述全局變量的地方使用的Block遭铺,生成的Block為_(kāi)NSConcreteGlobalBlock參數(shù)丽柿。例如
void (^blk)(void) = ^{printf("Global Block\n");};
int main(int argc, const char * argv[]) {
@autoreleasepool {
blk();
}
return 0;
}
C++
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __blk_block_func_0(struct __blk_block_impl_0 *__cself) {
printf("Global Block\n");}
static struct __blk_block_desc_0 {
size_t reserved;
size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
void (*blk)(void) = ((void (*)())&__global_blk_block_impl_0);
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
return 0;
}
我們可以看到Block用結(jié)構(gòu)體的成員變量isa的初始化如下:
impl.isa = &_NSConcreteGlobalBlock;
Block為_(kāi)NSConcreteGlobalBlock類對(duì)象具體總結(jié)如下:
1.記述全局變量的地方有Block語(yǔ)法時(shí)
2.Block語(yǔ)法的表達(dá)式中不使用應(yīng)截獲的自動(dòng)變量時(shí)
除此之外Blcok語(yǔ)法生成的Block為_(kāi)NSConcreteStackBlock。且設(shè)置在棧上魂挂。那_NSConcreteMallocBlock何時(shí)使用呢航厚?
配置在全局變量的Blcok,從變量作用域外也可以通過(guò)使用指針安全 的訪問(wèn)锰蓬。但設(shè)置在棧上的Blcok,如果其所屬的變量作用域結(jié)束眯漩,該Block就廢棄了芹扭。由于_ block變量也配置在棧上,同樣地赦抖,如果所屬的變量的作用域結(jié)束舱卡,則該 _blcok變量也會(huì)被廢棄。
Blcoks提供了將Block和_ _ block變量從棧上復(fù)制到堆上的方法來(lái)解決這個(gè)問(wèn)題队萤。如圖
復(fù)制到堆上的block將_NSConcreteMallocBlock對(duì)象寫入block的實(shí)體店變量
impl.isa = &_NSConcreteMallocBlock
對(duì)于已經(jīng)在堆上的Blcok以及程序數(shù)據(jù)區(qū)上的Blcok轮锥,調(diào)用copy會(huì)如何呢?
Block淺復(fù)制到堆上時(shí)對(duì)_ _block的影響
- 1個(gè)Block中使用 _ block變量要尔,當(dāng)Block從棧淺復(fù)制到堆上時(shí)舍杜,使用的所有 block也必定從棧上復(fù)制到堆上,此時(shí)Blcok持有 block變量赵辕。復(fù)制Blcok也對(duì)所使用的 _block 變量沒(méi)什么影響既绩。
- 在多個(gè)Block中實(shí)用化 _ block變量時(shí),在任何一個(gè)Blcok復(fù)制到堆上時(shí)还惠, block也必定從棧上復(fù)制到堆上并被該Blcok所持有饲握,當(dāng)剩下的Blcok從棧復(fù)制到堆上時(shí),被復(fù)制的Block持有 block蚕键,并增加 _block的引用計(jì)數(shù)救欧。*
什么時(shí)候棧上的Blcok會(huì)復(fù)制到堆呢
1.調(diào)用Block的copy實(shí)例方法
2.Blcok作為函數(shù)返回值返回時(shí)
3.將Block賦值給附有 _ _strong修飾符id類型的類或Blcok類型成員變量時(shí)
4.在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中傳遞Block時(shí)