序言:翻閱資料乏矾,學(xué)習(xí),探究迁杨,總結(jié)钻心,借鑒,謝謝探路者铅协,我只是個(gè)搬運(yùn)工捷沸。
參考、轉(zhuǎn)發(fā)資料:
http://www.cnblogs.com/chenxianming/p/5554395.html
http://www.reibang.com/p/ca6ac0ae93ad#
書籍:Objective-C高級(jí)編程
1. Block的本質(zhì)
Block的本質(zhì)其實(shí)是object對(duì)象
2. 內(nèi)部分析
在百度到的知識(shí)基本上都是使用clang(LLVM編譯器狐史,和GCC類似)來解析編譯的痒给。總結(jié)一下自己的理解骏全。
以下代碼為例:
int main(int argc, char * argv[]) {
void (^test)() = ^(){
};
test();
}
接下來我要用到一個(gè)命令clang src.m -rewrite-objc -o dest.cpp.這個(gè)意思是用clang編譯器對(duì)源文件src.m中的objective-c代碼轉(zhuǎn)換成C代碼放在dest.cpp文件苍柏。其實(shí)xcode編譯時(shí)也會(huì)幫我們轉(zhuǎn)換。我們這樣就可以dest.cpp在看到我們定義和調(diào)用的block轉(zhuǎn)換成C是怎么樣的姜贡。執(zhí)行命令后查看這個(gè)dest.cpp會(huì)發(fā)現(xiàn)有一大堆代碼试吁。下面我把對(duì)我們有用并能夠說清楚原理的關(guān)鍵貼上來并加以注釋:
//__block_imp: 這個(gè)是編譯器給我們生成的結(jié)構(gòu)體,每一個(gè)block都會(huì)用到這個(gè)結(jié)構(gòu)體
struct __block_impl {
void *isa; //對(duì)于本文可以忽略
int Flags; //對(duì)于本文可以忽略
int Reserved; //對(duì)于本文可以忽略
void *FuncPtr; //函數(shù)指針鲁豪,這個(gè)會(huì)指向編譯器給我們生成的下面的靜態(tài)函數(shù)__main_block_func_0
};
/*__main_block_impl_0:
是編譯器給我們?cè)趍ain函數(shù)中定義的block
void (^test)() = ^(){
};
生成的對(duì)應(yīng)的結(jié)構(gòu)體
*/
struct __main_block_impl_0 {
struct __block_impl impl; //__block_impl 變量impl
struct __main_block_desc_0* Desc; //__main_block_desc_0 指針潘悼,指向編譯器給我們生成的結(jié)構(gòu)體變量__main_block_desc_0_DATA __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { //結(jié)構(gòu)體的構(gòu)造函數(shù)
impl.isa = &_NSConcreteStackBlock; //說明block是棧blockimpl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;}
};
//__main_block_func_0: 編譯器根據(jù)block代碼生成的全局態(tài)函數(shù),會(huì)被賦值給impl.FuncPtr
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}
//__main_block_desc_0: 編譯器根據(jù)block代碼生成的block描述,主要是記錄下__main_block_impl_0結(jié)構(gòu)體大小
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; //這里就生成了__main_block_desc_0的變量__main_block_desc_0_DATA
//這里就是main函數(shù)了
int main(int argc, char * argv[]) {
// 以下代碼轉(zhuǎn)換之后爬橡,void (* test)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); //下面單獨(dú)講
// 以下代碼轉(zhuǎn)換之后治唤,test->FuncPtr(test);
((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test); //下面單獨(dú)講
}
- 總結(jié):
好了講到這里,就可以進(jìn)行一個(gè)中途簡(jiǎn)單性的總結(jié):忽略中間的復(fù)雜分支糙申,留下主線宾添,當(dāng)我們聲明一個(gè)Block變量a并為它賦值時(shí),其實(shí)就是創(chuàng)建一個(gè)函數(shù)指針ptrA,再根據(jù)Block a賦值的代碼生成一個(gè)靜態(tài)函數(shù)柜裸,而指針ptrA就指向這個(gè)靜態(tài)函數(shù)缕陕。Block a調(diào)用時(shí)就是使用函數(shù)指ptrA調(diào)用生成的靜態(tài)函數(shù)。
3. 獲取自動(dòng)變量的瞬間值
- block的定義:帶有自動(dòng)變量的匿名函數(shù)疙挺。然而怎么獲取自動(dòng)變量的瞬間值呢扛邑?
我們對(duì)最初的代碼進(jìn)行修改添加變量
int main(int argc, char * argv[]) {
int value = 1;
void (^test)() = ^(){
int valueTest = value;
};
test();
}
如上的內(nèi)部實(shí)現(xiàn)代碼:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// 這里是不同于上面的,多出來的铐然,但是其中使用的全局變量(包括全局靜態(tài)變量)不會(huì)生成對(duì)應(yīng)的參數(shù)對(duì)象蔬崩。只有自動(dòng)變量才會(huì)恶座。
int value;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value, int flags=0) : value(_value) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int value = __cself->value; // bound by copy
int valueTest = value; // 這里是不同于上面的,多出來的
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
int value = 1;
void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, value));
((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
}
- 理解一下:在Block外定義的自動(dòng)變量(Block中不使用的自動(dòng)變量不做處理)沥阳,將會(huì)隨著Block的創(chuàng)建跨琳,以形參的形式傳遞進(jìn)Block,在創(chuàng)建對(duì)應(yīng)Block方法的時(shí)候桐罕,結(jié)構(gòu)體中會(huì)生成對(duì)應(yīng)形參的變量用于儲(chǔ)存記錄形參的值脉让,通過構(gòu)造方法賦值。
你會(huì)發(fā)現(xiàn)功炮,在block生成的匿名函數(shù)中溅潜,block是用定義的變量去接收自動(dòng)變量的值并存儲(chǔ),在使用block生成的靜態(tài)函數(shù)static void __main_block_func_0(struct __main_block_impl_0 *__cself)
時(shí)死宣,傳入了是Block對(duì)象伟恶,通過拿取的是Block中的值。
3. __block 的實(shí)現(xiàn)原理(下一模塊就再對(duì)它的作用情況進(jìn)行細(xì)致的講解)
以下代碼為例:
int main(int argc, char * argv[]) {
__block int value = 1;
void (^test)() = ^(){
value = 2;
};
test();
int value1 = value;
}
一如既往毅该,我們看一下添加了_block內(nèi)部發(fā)生了什么變化博秫。
//根據(jù)帶__block修飾符的變量value,編譯器給我們生成了個(gè)結(jié)構(gòu)體
struct __Block_byref_value_0 {
void *__isa;
__Block_byref_value_0 *__forwarding; //這個(gè)會(huì)指向被創(chuàng)建出來的__Block_byref_value_0實(shí)例
int __flags;
int __size;
int value;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_value_0 *value; //保存__Block_byref_value_0變量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_value_0 *_value, int flags=0) : value(_value->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_value_0 *value = __cself->value; // bound by ref
// 注意這里
(value->__forwarding->value) = 2;
}
//這兩個(gè)函數(shù)分別會(huì)在test block 被拷貝到堆和釋構(gòu)時(shí)調(diào)用的眶掌,作用是對(duì)__Block_byref_value_0實(shí)例的內(nèi)存進(jìn)行管理挡育,至于怎么管理,這里就不討論了朴爬,這里就會(huì)調(diào)用上面導(dǎo)出來的接口即寒。
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->value, (void*)src->value, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->value, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); //回調(diào)函數(shù)指針,會(huì)被賦值為__main_block_copy_0
void (*dispose)(struct __main_block_impl_0*); //回調(diào)函數(shù)指針召噩,會(huì)被賦值為__main_block_dispose_0
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0}; /*{ 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0}母赵,這句就是創(chuàng)建一個(gè)例的意思,這是結(jié)構(gòu)體的一種構(gòu)造方式具滴。*/
int main(int argc, char * argv[]) {
/*我們定義的__block int value轉(zhuǎn)換后并不是一個(gè)簡(jiǎn)單的棧變量凹嘲,而會(huì)是新建的__Block_byref_value_0堆變量*/
__attribute__((__blocks__(byref))) __Block_byref_value_0 value = {(void*)0,(__Block_byref_value_0 *)&value, 0, sizeof(__Block_byref_value_0), 1};
void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_value_0 *)&value, 570425344));
((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
//最后面使這句int value1 = value;使用value時(shí),在我們表面看到是好像是使用main函數(shù)里的一個(gè)局部棧變量构韵,其實(shí)不是周蹭,使用的是堆里面的容int value1 = (value.__forwarding->value);
}
- 總結(jié):我們不能看出,使用了_block就多了修飾value的結(jié)構(gòu)體
_Block_byref_value_0
疲恢,而最根本的原因是用了_block修飾的變量被從棧區(qū)copy到堆區(qū)中了凶朗,這樣,就不會(huì)被立即釋放了(而static修飾的靜態(tài)變量存儲(chǔ)在靜態(tài)區(qū)显拳、全局變量不需要用_block修飾是一個(gè)道理)棚愤。 - 注意:其中有個(gè)細(xì)節(jié)需要理解,為什么在
__Block_byref_value_0
的結(jié)構(gòu)體中還有一個(gè)一樣__Block_byref_value_0
結(jié)構(gòu)體變量杂数。其實(shí)是有原因的宛畦,__Block_byref_value_0
中的_forwarding有什么作用矛绘?_forwarding,指向自己的指針刃永,當(dāng)從棧copy到堆時(shí),指向堆上的block羊精,這也是為什么(value->_forwarding->value) = 2
反復(fù)指向的原因斯够。
_forwarding 的原理如下圖
4. Block的存儲(chǔ)區(qū)域
- Block和_block變量的實(shí)質(zhì)
- Block:棧上Block的結(jié)構(gòu)體實(shí)例。
- _block:棧上_block變量的結(jié)構(gòu)體實(shí)例喧锦。
- Block的類:
- _NSConcreteStackBlock 棧區(qū)
之前我們使用的都是_NSConcreteStackBlock(棧區(qū))類型的Block读规。 - _NSConcreteGlobalBlock 數(shù)據(jù)區(qū)(靜態(tài)區(qū))
_NSConcreteGlobalBlock稍微解釋一下:在記述全局變量的地方使用Block語法時(shí),生成的Block燃少。
滿足條件:- 記述全局變量的地方有Block語法時(shí)束亏。
- Block語法的表達(dá)式中不使用應(yīng)截獲的自動(dòng)變量時(shí)。
例如:
void (^blk)(void) = ^(printf("HelloWorld")) ;
int main(){
}
- _NSConcreteMallocBlock 堆區(qū)
Blocks提供了將Block和_block變量從棧上賦值到堆上的方法來解決這個(gè)問題阵具。將配置在棧上的Block賦值到棧上碍遍,這樣即使Block語法計(jì)數(shù)的變量作用域結(jié)束,堆上的Block還可以繼續(xù)存在阳液,
例子:
/**
* _Block_copy函數(shù)
* 將棧上的Block復(fù)制到堆上
* 復(fù)制后怕敬,將堆上的地址作為指針賦值給變量tmp
*/
tmp = _Block_copy(tmp) ;
注意:Block從棧區(qū)copy到堆區(qū)是相當(dāng)消耗CPU的。
不同類型Block執(zhí)行_Block_copy的圖標(biāo)說明帘皿。
Block的類 | 副本源的配置存儲(chǔ)域 | 賦值效果 |
---|---|---|
__NSConcreteStackBlock | 棧 | 從棧區(qū)復(fù)制到堆區(qū) |
__NSConcreteGlobalBlock | 程序的數(shù)據(jù)區(qū)域(靜態(tài)區(qū)) | 什么都不做 |
__NSConcreteMallocBlock | 堆區(qū) | 引用計(jì)數(shù)增加 |
5. 具體例子的分析东跪,并用以上講解的知識(shí)解釋
- 為什么在Block方法外修改自動(dòng)變量不會(huì)影響B(tài)lock內(nèi)部的使用。
以下代碼為例:
int value = 0 ;
void (^test)() = ^(){
// 結(jié)果:value = 0
NSLog(@"value = %d",value) ;
};
value = 2 ;
test();
Block方法獲取的是自動(dòng)變量的瞬間值鹰溜。
- 為什么Block不能直接修改自動(dòng)變量虽填。
以下代碼為例:
int value = 0 ;
void (^test)() = ^(){
// 結(jié)果:value = 0
value = 4 ;
};
test();
這段代碼在編寫編譯的時(shí)候就會(huì)報(bào)錯(cuò),因?yàn)樽詣?dòng)變量值截獲只能保存執(zhí)行Block語法瞬間的值曹动,保存后就不能改寫該值斋日。其實(shí)這段代碼在編寫的規(guī)范上沒有任何錯(cuò)誤,但是因?yàn)樵趯?shí)現(xiàn)上不能改寫被截獲自動(dòng)變量的值仁期,所以當(dāng)編譯器在編譯過程中檢出給被截獲自動(dòng)變量賦值的操作時(shí)桑驱,便產(chǎn)生編譯錯(cuò)誤。
- 雖然在Block中不能對(duì)自動(dòng)變量直接修改跛蛋,但是能對(duì)自動(dòng)變量做其他操作熬的。
例如:
NSMutableArray *array_m = [NSMutableArray array] ;
[array_m addObject:@1] ;
void (^test)() = ^(){
// 結(jié)果array_m: (1,2)
[array_m addObject:@2] ;
NSLog(@"array_m: %@",array_m) ;
};
test();
因?yàn)閍rray_m是個(gè)指針對(duì)象,和Block中value都同時(shí)指向同一個(gè)類的實(shí)例化區(qū)域赊级,所以可以在不改變value指針地址的基礎(chǔ)上進(jìn)行操作押框。但是你要是改變array_m的指針地址例如
array_m = nil 就會(huì)出現(xiàn)錯(cuò)誤。
- 使用_blcok解決不能修改Block外自動(dòng)變量的值理逊。
以下代碼為例:
__block int value = 0 ;
void (^test)() = ^(){
value = 4 ;
// 結(jié)果:value: 4
NSLog(@"value: %d",value) ;
};
test();
我們可以參照上面簡(jiǎn)述的代碼內(nèi)容:
//根據(jù)帶__block修飾符的變量value橡伞,編譯器給我們生成了個(gè)結(jié)構(gòu)體
struct __Block_byref_value_0 {
void *__isa;
__Block_byref_value_0 *__forwarding; //這個(gè)會(huì)指向被創(chuàng)建出來的__Block_byref_value_0實(shí)例
int __flags;
int __size;
int value;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_value_0 *value; //保存__Block_byref_value_0變量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_value_0 *_value, int flags=0) : value(_value->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_value_0 *value = __cself->value; // bound by ref
// 注意這里
(value->__forwarding->value) = 4;
}
你會(huì)發(fā)現(xiàn)對(duì)了一個(gè)value在Block中變成了一個(gè)結(jié)構(gòu)體盒揉,這就是奧秘,從_main_block_func_0
方法來看兑徘,你在Block的操作結(jié)構(gòu)體指針永遠(yuǎn)都不會(huì)變和自動(dòng)變量一致刚盈,而是對(duì)_Block_byref_value_0
的value進(jìn)行操作,所以可以賦值挂脑。
- 疑惑點(diǎn):
為什么自動(dòng)變量值截獲只能保存執(zhí)行Block語法瞬間的值藕漱,保存后就不能改寫該值?
首先Block中的value(用來記錄的對(duì)象我用value統(tǒng)稱)對(duì)象的地址是不允許改變的崭闲。為什么靜態(tài)變量的方式也適用于自動(dòng)變量的訪問肋联,但是我們沒有這樣做呢?
實(shí)際上刁俭,在由Block語法生成的值Block上橄仍,可以存在超過其變量作用域的被截獲對(duì)象的自動(dòng)變量。變量作用于結(jié)束的同時(shí)牍戚,原來的自動(dòng)變量被廢棄侮繁,因此Block超過變量作用域而存在的變量同靜態(tài)變量一樣,將不能通過指針訪問原來的自動(dòng)變量如孝。而這個(gè)可以存在指的就是NSConcreteMallocBlock鼎天。NSConcreteMallocBlock的訪問是通過_forwarding實(shí)現(xiàn)的,只要棧上的結(jié)構(gòu)體實(shí)例成員變量_forwarding指向堆上的結(jié)構(gòu)體實(shí)例暑竟,那么不管是在棧上的_block變量還是從堆上的_block變量都能訪問斋射。-
為什么局部靜態(tài)變量不需要任何操作就能夠在Block塊中使用操作,而自動(dòng)變量需要但荤?
從廣義的角度講的話罗岖,就是和變量在內(nèi)存當(dāng)中的存儲(chǔ)位置和其作用域。從內(nèi)部實(shí)現(xiàn)原理來說腹躁,請(qǐng)看下面代碼桑包,轉(zhuǎn)換之后挑取需要的代碼:- 自動(dòng)變量方法
// 方法
int main(int argc, char * argv[]) {
int value = 1 ;
void (^test)() = ^(){
printf(value) ;
};
test();
}
// 轉(zhuǎn)換之后__main_block_impl_0結(jié)構(gòu)體中
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// 注意這里 value變量不帶*
int value;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value, int flags=0) : value(_value) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 靜態(tài)局部變量
// 方法
int main(int argc, char * argv[]) {
static int value = 1 ;
void (^test)() = ^(){
printf(value) ;
};
test();
}
// 轉(zhuǎn)換之后__main_block_impl_0結(jié)構(gòu)體中
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// 注意這里 value變量帶*
int *value;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value, int flags=0) : value(_value) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
二者的區(qū)別:value對(duì)象,一個(gè)帶*是指針纺非,一個(gè)不帶指針是個(gè)基本變量類型哑了。
- 總結(jié):
- 在都是局部變量的情況下(這里不考慮全局變量、全局靜態(tài)變量烧颖,為了在Block塊中使用這些變量時(shí)弱左,Block內(nèi)部是不做如何處理都沒問題的),我們不能修改賦值定義在Block結(jié)構(gòu)體
_main_block_impl_0
中的變量value(我有時(shí)候會(huì)用value統(tǒng)稱變量名)炕淮,弄清楚基礎(chǔ)變量和指針變量對(duì)你理解這句話拆火,甚至對(duì)各種情況的執(zhí)行的理解都有幫助。 - 要明白不做任何處理和修飾的自動(dòng)變量和Block中的value(Block中用來存儲(chǔ)自動(dòng)變量值的變量,我統(tǒng)稱value)都是存儲(chǔ)在棧區(qū)的基本變量们镜,這兩個(gè)變量的存儲(chǔ)區(qū)域的指針地址不同币叹,所以即使我們?cè)贐lock中修改了value的值,但是也不能修改自動(dòng)變量模狭。但是不管是static修飾自動(dòng)變量還是_block修飾自動(dòng)變量都是_block中存儲(chǔ)自動(dòng)變量的value變成了一個(gè)指針變量颈抚。所以你修改的指針地址,而是值嚼鹉。
- 要想在Block中操作局部變量邪意,需要保存局部變量和Block中存儲(chǔ)變量的指針地址一致,不然不能進(jìn)行復(fù)制操作反砌,但是可以不改變指針地址的操作例如數(shù)組添加元素。
- 在都是局部變量的情況下(這里不考慮全局變量、全局靜態(tài)變量烧颖,為了在Block塊中使用這些變量時(shí)弱左,Block內(nèi)部是不做如何處理都沒問題的),我們不能修改賦值定義在Block結(jié)構(gòu)體
6. Block的循環(huán)引用
- 說明:
如果在Block中使用附有_strong修飾符的對(duì)象類型自動(dòng)變量萌朱,那么當(dāng)Block從棧復(fù)制到堆區(qū)時(shí)宴树,該對(duì)象為Block所持有,這樣容易引起循環(huán)引用晶疼。
typedef void (^Test)(void);
@interface ViewController ()
{
Test test ;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
test = ^{
NSLog(@"self: %@",self) ;
} ;
}
執(zhí)行的Block語法使用附有_strong修飾符的id類型變量self酒贬,因此通過Block語法生成在棧上的Block此時(shí)由棧區(qū)復(fù)制到堆區(qū),并持有所使用的self翠霍。
如圖:
接下來解釋一下上面一句話:
// 轉(zhuǎn)換之前的代碼
NSMutableArray *array_m = [[NSMutableArray alloc]init] ;
void (^test)() = ^(){
[array_m addObject:@(1)] ;
};
test();
// 轉(zhuǎn)換之后的代碼锭吨,省略部分代碼
__main_block_impl_0{
id _strong array_m ;
}
解釋:Block中使用的賦值給附有_strong修飾符的自動(dòng)變量的對(duì)象和復(fù)制到堆上的_block變量由于被堆上的Block持有,因此可超出其變量作用域而存在(有點(diǎn)復(fù)雜寒匙,不做解釋)零如。
解決:用_weak修飾self,來解決循環(huán)引用锄弱。
- 解決方式
- 使用_weak修飾self考蕾,來解決循環(huán)引用。
typedef void (^Test)(void);
@interface ViewController ()
{
Test test ;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak ViewController *weakSelf = self ;
test = ^{
NSLog(@"self: %@", weakSelf) ;
} ;
}
- 使用_blcok修飾self会宪,來解決循環(huán)引用肖卧。
typedef void (^Test)(void);
@interface ViewController ()
{
Test test ;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block ViewController *blockSelf = self ;
test = ^{
NSLog(@"self: %@", blockSelf) ;
// 注意
blockSelf = nil ;
} ;
// 注意
test() ;
}
注意:需要注意兩點(diǎn)。
1掸鹅、一定要在Block中把_block變量置nil 塞帐。
2、一定要執(zhí)行這個(gè)Block方法 巍沙。
原理圖:
- __block的特點(diǎn)
- 通過_block變量可控制對(duì)象的持有時(shí)間葵姥。優(yōu)點(diǎn)
- 為了避免循環(huán)引用必須執(zhí)行Block。缺點(diǎn)