相關(guān)文章
- (一)Block的實(shí)質(zhì)初探
- (二)Block之存儲(chǔ)域 NSConcreteStackBlock崭歧,NSConcreteGlobalBlock,NSConcreteMallocBlock
- (三)Block之截獲變量和對(duì)象
- (四)Block之 __block修飾符及其存儲(chǔ)域
引言
int i = 0; // 不加__block修飾符將編譯錯(cuò)誤
void (^blk)() = ^() {
i = 1; // error5饫怠<萑佟!F张荨播掷!
};
當(dāng)我們?cè)贐lock語(yǔ)法內(nèi)修改自動(dòng)變量時(shí),沒有加__block修飾符撼班,將會(huì)得到一個(gè)編譯錯(cuò)誤
為了保存Block對(duì)值的操作歧匈,我們可以定義:
- 全局變量
- 靜態(tài)全局變量
- 靜態(tài)局部變量
int global_var = 1; // 全局變量
static int static_global_var = 2; // 靜態(tài)全局變量
{
static int static_var = 3; // 靜態(tài)局部變量
void (^blk)() = ^() {
global_var++;
static_global_var++;
static_var++;
printf("%d\n", global_var); // 輸出2
printf("%d\n", static_global_var); // 輸出3
printf("%d\n", static_var); // 輸出4
};
blk();
}
輸出結(jié)果如我們所愿,值修改成功了
我們進(jìn)行編碼轉(zhuǎn)換砰嘁,看下發(fā)生了什么事情
int global_var = 1;
static int static_global_var = 2;
/ * Block結(jié)構(gòu)體 */
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *static_var;
// ......省略構(gòu)造函數(shù)
};
/ * Block方法 */
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *static_var = __cself->static_var; // 注意這里<!0妗U迕帷!
global_var++;
static_global_var++;
(*static_var)++; // 注意這里C逖簟?纳摺!J臁秀撇!
printf("%d\n", global_var);
printf("%d\n", static_global_var);
printf("%d\n", (*static_var));
}
可見,對(duì)全局變量
和靜態(tài)全局變量
的訪問依然不作變化向族,靜態(tài)局部變量
則使用指針對(duì)其訪問(其指針傳遞給__main_block_impl_0
Block結(jié)構(gòu)體呵燕,通過(guò)保存的指針在Block語(yǔ)法內(nèi)來(lái)訪問自動(dòng)變量)
我們也可以給變量加上__block
修飾符,達(dá)到同樣的目的
__block修飾符
__block int i = 10; // 不加__block修飾符將編譯錯(cuò)誤
void (^blk)() = ^() {
i = 20;
};
添加__block
修飾符后件相,如愿達(dá)到了目的再扭,接下來(lái)看看發(fā)生了什么事情
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding; // 注意這里Q醪浴!7悍丁:蛞!
int __flags;
int __size;
int i;
};
/ * Block結(jié)構(gòu)體 */
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // 注意這里6氐!9淇D!
構(gòu)造函數(shù)
__main_block_impl_0(void *fp,
struct __main_block_desc_0 *desc,
__Block_byref_i_0 *_i,
int flags=0) : i(_i->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
/ * Block方法 */
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_i_0 *i = __cself->i; // 注意這里1缁>宓选!3研埂患整!
(i->__forwarding->i) = 20;// 注意這里!E缰凇8餮琛!到千!
}
/ * __block int i = 0; 轉(zhuǎn)換后代碼*/
__Block_byref_i_0 i = {0, //__isa
(__Block_byref_i_0 *)&i, // __forwarding 注意這里2场!c舅摹膀息!
0, // flag
sizeof(__Block_byref_i_0), // size
10 // 變量i
};
- 添加
__block
修飾符后, 代碼量增加了不少,自動(dòng)變量i
被轉(zhuǎn)換成了結(jié)構(gòu)體__Block_byref_i_0
了赵,值被保存在了這個(gè)結(jié)構(gòu)體內(nèi)部 - 和上面的對(duì)
靜態(tài)自動(dòng)變量
賦值(*static_var)++;
潜支,使用指向改變量的指針不同,__block
的賦值操作變成了(i->__forwarding->i) = 20;
- 從代碼轉(zhuǎn)換結(jié)果看
__Block_byref_i_0 i
變量的初始化柿汛,__forwarding
被賦值(__Block_byref_i_0 *)&i,
冗酿,也就是__forwarding
指針間接訪問,這個(gè)指針指向自身苛茂,如下圖所示
那么已烤,為什么會(huì)有__forwarding
呢? 下面將作出解釋
__block變量存儲(chǔ)域
已知妓羊,使用了__block變量的Block從棧復(fù)制到堆時(shí)胯究,__block變量也會(huì)受到影響,總結(jié)如下
__block變量存儲(chǔ)域 | Block從棧復(fù)制到堆 |
---|---|
棧 | 從棧復(fù)制到堆并被Block持有 |
堆 | 被Block持有 |
- 當(dāng)棧上的Block被復(fù)制到堆上時(shí)躁绸,__block變量也會(huì)隨之復(fù)制裕循,并且Block持有該變量
- 多個(gè)Block中使用__block變量時(shí)臣嚣,任何一個(gè)Block被復(fù)制到堆時(shí),__block變量也會(huì)一并復(fù)制到堆并被持有剥哑,其余Block被復(fù)制時(shí)硅则,僅需要__block 變量的引用計(jì)數(shù)
- 和使用引用計(jì)數(shù)一樣,在堆上的Block被廢棄株婴,它所引用的__block變量將被釋放
理解Block作用域之后怎虫,我們發(fā)現(xiàn)這和OC引用計(jì)數(shù)方式管理方式一樣,使用__block修飾符來(lái)持有對(duì)象困介,當(dāng)Block被廢棄之后大审,__block修飾變量也隨之釋放
當(dāng)Block被復(fù)制到堆之后,會(huì)將自身的__forwarding
指針更新座哩,依然指向“最新”的自己徒扶,這樣就保證了在棧上或者堆上都能正確訪問對(duì)應(yīng)變量
而__forwarding
指針的作用正在這里,上文的
__Block_byref_i_0 *i = __cself->i;
(i->__forwarding->i) = 20;
- 當(dāng)Block處于棧時(shí)根穷,__forwarding指向自身姜骡,
i->__forwarding->i
訪問的是棧上的結(jié)構(gòu)體的變量i
- 當(dāng)Block被復(fù)制到堆時(shí),
i->__forwarding->i
訪問的是堆上的結(jié)構(gòu)體的變量i
由此
__forwarding 保證在棧上或者堆上都能正確訪問對(duì)應(yīng)變量