上文講了一下__block的原理,但是關(guān)于__block還有一些其他東西,這篇文章就來慢慢講述。
一. __block變量的內(nèi)存管理
通過上文我們知道,block捕獲對象類型的auto變量就會多出兩個函數(shù)用于做內(nèi)存管理操作(__main_block_copy_0和__main_block_dispose_0),如下:
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 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*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
block也會使用這兩個函數(shù)管理__block修飾的變量的內(nèi)存,這也從側(cè)面證明“編譯器會將__block變量包裝成一個對象”,這句話是對的,因為只有對象才需要內(nèi)存管理。
下面我們就研究使用__block修飾的變量的內(nèi)存管理:
- 當(dāng)block在棧上時,并不會對__block變量產(chǎn)生強(qiáng)引用
- 當(dāng)block被copy到堆時(自己拷貝的或者ARC下系統(tǒng)自動拷貝的)
會調(diào)用block內(nèi)部的copy函數(shù)
copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
_Block_object_assign函數(shù)會對__block變量形成強(qiáng)引用(retain)
如下圖:

解釋:剛開始的時候,__block變量和block0、block1肯定都在棧區(qū),假如他們同時使用__block變量。
當(dāng)把block0復(fù)制到堆區(qū),也會把__block變量復(fù)制到堆區(qū),并且block0強(qiáng)引用__block變量。
再把block1復(fù)制到堆區(qū),就不會再次復(fù)制__block變量了(因為已經(jīng)拷貝過了),這時候block0和block1都會強(qiáng)引用著__block變量。
- 當(dāng)block從堆中移除時
會調(diào)用block內(nèi)部的dispose函數(shù)
dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
_Block_object_dispose函數(shù)會自動釋放引用的__block變量(release)
如下圖:

解釋:當(dāng)沒有block引用著__block變量,__block變量才會被釋放。
為什么要這么管理呢?
因為__block變量是個對象,block內(nèi)部使用了它,所以需要block來管理它的內(nèi)存。
二. 對象類型的auto變量和__block變量的區(qū)別
(static修飾的變量和全局變量在全局區(qū))
先把下面代碼轉(zhuǎn)成C++代碼:
int main(int argc, const char * argv[]) {
@autoreleasepool {
int no = 20;
__block int age = 10;
NSObject *object = [[NSObject alloc] init];
__weak NSObject *weakObject = object;
MJBlock block = ^{
age = 20;
NSLog(@"%d", no);
NSLog(@"%d", age);
NSLog(@"%p", weakObject);
};
block();
}
return 0;
}
轉(zhuǎn)成的C++代碼:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int no;
NSObject *__weak weakObject;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _no, NSObject *__weak _weakObject, __Block_byref_age_0 *_age, int flags=0) : no(_no), weakObject(_weakObject), age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->weakObject, (void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
可以發(fā)現(xiàn),結(jié)構(gòu)體多了三個成員,分別對應(yīng)捕獲的三個auto變量,如下??:
int no;
__Block_byref_age_0 *age; // by ref
NSObject *__weak weakObject;
1. 對象類型的auto變量、__block變量
相同點:
- 當(dāng)block在棧上時,對它們都不會產(chǎn)生強(qiáng)引用
- 當(dāng)block拷貝到堆上時,都會通過copy函數(shù)來處理它們
對象類型的auto變量(假設(shè)變量名叫做p)
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
__block變量(假設(shè)變量名叫做a)
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
- 當(dāng)block從堆上移除時,都會通過dispose函數(shù)來釋放它們
對象類型的auto變量(假設(shè)變量名叫做p)
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
__block變量(假設(shè)變量名叫做a)
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
不同點:
- 對于對象類型的auto變量,_Block_object_assign函數(shù)會根據(jù)auto變量的修飾符(__strong、__weak、__unsafe_unretained)做出相應(yīng)的操作,形成強(qiáng)引用(retain)或者弱引用
- 對于__block變量:_Block_object_assign函數(shù)只會產(chǎn)生強(qiáng)引用(retain)
2. 普通的的auto變量
例如:int a = 10,就是值引用,然后放到__main_block_impl_0結(jié)構(gòu)體里面。
三. __forwarding指針的作用
我們知道__Block_byref_age_0結(jié)構(gòu)體中的__forwarding指針存的是自己的地址,當(dāng)我們想要訪問age,需要先通過age結(jié)構(gòu)體中的__forwarding獲取自己,然后再獲取age:
(age->__forwarding->age) = 20;
為什么設(shè)計這么奇怪呢?如下圖:

解釋:如果棧上的block復(fù)制到堆上了,那么棧上堆上肯定都有一塊內(nèi)存。如果我們想把20賦值到堆上的block,如果不用__forwarding指針,訪問棧上的block,就會把20賦值到棧上的block。如果使用__forwarding指針,不管訪問的block在哪,最后賦值到的一定是堆上的block。
四. __block修飾的對象類型
以前我們是使用__block修飾基本數(shù)據(jù)類型,如果使用__block修飾對象類型會怎么樣呢?
如下代碼,內(nèi)部會發(fā)生什么呢?
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
//不能反過來(如:__weak __block),因為__weak只能修飾OC對象
__block __weak MJPerson *weakPerson = person;
MJBlock block = ^{
NSLog(@"%p", weakPerson);
};
block();
}
return 0;
}
轉(zhuǎn)成C++代碼,如下:
struct __Block_byref_weakPerson_0 {
void *__isa; // 8
__Block_byref_weakPerson_0 *__forwarding; // 8
int __flags; // 4
int __size; // 4
void (*__Block_byref_id_object_copy)(void*, void*); // 8
void (*__Block_byref_id_object_dispose)(void*); // 8
MJPerson *__weak weakPerson; //外面是弱指針,這里就是弱指針 (以前這里直接是int age;的)
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // 默認(rèn)強(qiáng)指針
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
首先,發(fā)現(xiàn)__main_block_impl_0里面默認(rèn)有一個強(qiáng)指針指向__Block_byref_weakPerson_0結(jié)構(gòu)體。由于外面是用__weak修飾的,所以__Block_byref_weakPerson_0結(jié)構(gòu)體里面有一個弱指針指向person對象,如下圖:

上面的代碼還可以看出__Block_byref_weakPerson_0結(jié)構(gòu)體里面多了兩個方法copy和dispose方法。(這兩個方法在以前我們使用__block修飾基本數(shù)據(jù)類型的時候是沒有的)
在__Block_byref_weakPerson_0結(jié)構(gòu)體創(chuàng)建里面(就是__block __weak MJPerson *weakPerson = person;的底層實現(xiàn)),傳入了copy和dispose兩個函數(shù)地址,如下:
//copy block內(nèi)存管理相關(guān)
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->weakPerson, 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*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
MJPerson *person = ((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MJPerson"), sel_registerName("alloc")), sel_registerName("init"));
//__block __weak MJPerson *weakPerson = person;的底層實現(xiàn)
//就是創(chuàng)建__Block_byref_weakPerson_0結(jié)構(gòu)體
__Block_byref_weakPerson_0 weakPerson = {(void*)0,(__Block_byref_weakPerson_0 *)&weakPerson, 33554432, sizeof(__Block_byref_weakPerson_0),
__Block_byref_id_object_copy_131, //傳入copy方法的地址
__Block_byref_id_object_dispose_131, //傳入dispose方法的地址
person};
MJBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_weakPerson_0 *)&weakPerson, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
struct __Block_byref_weakPerson_0 {
void *__isa; // 8
__Block_byref_weakPerson_0 *__forwarding; // 8
int __flags; // 4
int __size; // 4
void (*__Block_byref_id_object_copy)(void*, void*); // 8
void (*__Block_byref_id_object_dispose)(void*); // 8
MJPerson *__weak weakPerson; //外面是弱指針,這里就是弱指針 (以前這里直接是int age;的)
};
//copy 對象內(nèi)存管理相關(guān)
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
//dispose
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
觀察上面的copy,發(fā)現(xiàn)它里面也有調(diào)用_Block_object_assign函數(shù),(char*)dst就是結(jié)構(gòu)體自己的地址值,但是它卻+40 (8+8+4+4+8+8),從上上面我們分析可知:(char*)dst+40就是“MJPerson *__weak weakPerson;”的地址值。然后_Block_object_assign函數(shù)再根據(jù)傳入的對象是強(qiáng)還是弱,再決定retain還是弱引用。所以上圖的第二根指針的強(qiáng)弱,取決于外面修飾對象的的是強(qiáng)指針還是弱指針?
當(dāng)block銷毀的時候,block內(nèi)部會調(diào)用__main_block_dispose_0函數(shù),然后上圖的第一根線就沒了。第一根線就沒了,__Block_byref_weakPerson_0也會銷毀,它也會調(diào)用自己里面的__Block_byref_id_object_dispose_131函數(shù),__Block_byref_id_object_dispose_131函數(shù)再把person引用計數(shù)器減一(或者銷毀)。
總結(jié):
對于block:
- 如果block是在棧上,將不會對__Block_byref_weakPerson_0產(chǎn)生強(qiáng)引用
- 如果棧上的block被拷貝到堆上
_Block_object_assign函數(shù)會對__Block_byref_weakPerson_0產(chǎn)生強(qiáng)引用 - 如果堆上的block被移除
_Block_object_dispose函數(shù)會對__Block_byref_weakPerson_0產(chǎn)生弱引用或者移除
對于__block修飾的對象類型:
- 當(dāng)__block變量在棧上時,不會對指向的對象產(chǎn)生強(qiáng)引用
- 當(dāng)__block變量被copy到堆時(自己拷貝的或者ARC下系統(tǒng)自動拷貝的)
會調(diào)用__block變量內(nèi)部的copy函數(shù)
copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
_Block_object_assign函數(shù)會根據(jù)所指向?qū)ο蟮男揎椃╛_strong、__weak、__unsafe_unretained)做出相應(yīng)的操作,形成強(qiáng)引用(retain)或者弱引用(注意:這里僅限于ARC時會retain,MRC時不會retain,一直是弱的。這個特例只會在MRC并且是__block修飾對象類型才有) - 如果__block變量從堆上移除
會調(diào)用__block變量內(nèi)部的dispose函數(shù)
dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
_Block_object_dispose函數(shù)會自動釋放指向的對象(release)
下面驗證上面的“注意”:
將項目切換成MRC環(huán)境,使用__block修飾對象類型,如下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block MJPerson *person = [[MJPerson alloc] init];
MJBlock block = [^{
NSLog(@"%p", person);
} copy];
[person release];
block(); //在此處打斷點
[block release];
}
return 0;
}
在上面代碼斷點處,可以發(fā)現(xiàn)打印:“[MJPerson dealloc]”,此時block還沒釋放,但是person卻不在了,驗證了我們上面說的“注意”。根據(jù)這一點,在MRC環(huán)境下,我們可以使用__block來解決循環(huán)引用,這個后面會說到。
解釋如上代碼,當(dāng)block被拷貝到堆上的時候,__block修飾的person也會被拷貝到堆上,這時候會調(diào)用__block修飾的變量內(nèi)部的copy函數(shù),MRC環(huán)境下,copy函數(shù)內(nèi)部只會對person對象產(chǎn)生弱引用。如下圖:

如果將上面代碼的__block去掉,那么在斷點處就不會打印:“[MJPerson dealloc]”,之后block釋放之后person才會釋放。這時候就是block直接捕獲person對象了,如下圖:

五. block循環(huán)引用
1. 循環(huán)引用產(chǎn)生的原因
如下代碼:
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
person.age = 10;
person.block = ^{
NSLog(@"age is %d", person.age);
};
}
NSLog(@"111111111111");
return 0;
}
首先“ MJPerson *person = [[MJPerson alloc] init];”執(zhí)行完,會有個person強(qiáng)指針指向MJPerson。MJPerson里面有個_block,當(dāng)執(zhí)行完 person.block = ^{NSLog(@"age is %d", person.age);},MJPerson和block之間的循環(huán)引用就會產(chǎn)生,如下圖:

(block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象)
同理,self和block之間的循環(huán)引用如下圖:

因為self是局部變量,所以block也會捕獲self,也會造成循環(huán)引用。block底層C++代碼如下:
struct __MJPerson__test_block_impl_0 {
struct __block_impl impl;
struct __MJPerson__test_block_desc_0* Desc;
MJPerson *const __strong self; //block內(nèi)部有個self強(qiáng)指針,指向self對象
__MJPerson__test_block_impl_0(void *fp, struct __MJPerson__test_block_desc_0 *desc, MJPerson *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
2. ARC如何解決循環(huán)引用
① 用__weak解決
首先,我們想一下,如何解決循環(huán)引用,肯定是把其中一根線變成虛線就可以了,但是把哪根線變成虛線呢?我們想了下,person中的block屬性不能變成虛的,因為person要擁有block,那么只能把block里面的person變成虛線了,怎么把block里面的person變成虛線呢?其實就是在上面的代碼,把__strong變成__weak就好了,前面我們已經(jīng)學(xué)過了,在外面用__weak修飾,里面就會變成__weak了,如下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
person.age = 10;
//__weak MJPerson *weakPerson = person; //這樣寫死了,不推薦
__weak typeof(person) weakPerson = person;
person.block = ^{
NSLog(@"age is %d", weakPerson.age);
};
}
NSLog(@"111111111111");
return 0;
}
這時候block底層C++代碼如下:
struct __MJPerson__test_block_impl_0 {
struct __block_impl impl;
struct __MJPerson__test_block_desc_0* Desc;
MJPerson *const __weak weakPerson; //weakPerson弱指針
__MJPerson__test_block_impl_0(void *fp, struct __MJPerson__test_block_desc_0 *desc, MJPerson *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
使用__weak后圖示如下:

解釋:當(dāng){}里面的代碼執(zhí)行完,MJPerson *person局部變量就會消失,person指向MJPerson的線就會消失,這時候沒有實線指向MJPerson,MJPerson就會銷毀,MJPerson銷毀后,MJPerson指向block的線就會消失,這時候沒有實線指向block,block也會銷毀,循環(huán)引用解除。
② 用__unsafe_unretained解決
上面的代碼,把__weak替換成__unsafe_unretained也能解決循環(huán)引用,代碼就省略了。
它們有什么區(qū)別呢?直接看字面意思,__weak:不會產(chǎn)生強(qiáng)引用,__unsafe_unretained:不會產(chǎn)生強(qiáng)引用,不安全。
__weak:不會產(chǎn)生強(qiáng)引用,指向的對象銷毀時,會自動讓指針置為nil
__unsafe_unretained:不會產(chǎn)生強(qiáng)引用,不安全,指向的對象銷毀時,指針存儲的地址值不變,這時候如果再去訪問指針指向的地址就會報野指針錯誤
如下圖,如果使用__weak,當(dāng)weakPerson指向的對象銷毀時,會把weakPerson置為nil,使用__unsafe_unretained就不會。

③ 用__block解決(必須要調(diào)用block)
int main(int argc, const char * argv[]) {
@autoreleasepool {
//使用__block解決循環(huán)引用
__block MJPerson *person = [[MJPerson alloc] init];
person.age = 10;
person.block = ^{
NSLog(@"age is %d", person.age);
person = nil;
};
person.block();
}
NSLog(@"111111111111");
return 0;
}
如上代碼,即可解決循環(huán)引用。
首先我們分析這里的循環(huán)引用是如何產(chǎn)生的。
前面我們說過使用__block修飾對象,block內(nèi)部會捕獲__block變量,__block變量內(nèi)部又有person對象,而且默認(rèn)是強(qiáng)引用,對象又擁有block,所以block、__block變量、person對象之間會有循環(huán)引用,示意圖如下:

所以,如果想要解決__block的循環(huán)引用,我們可以調(diào)用block,并且在block里面把person對象置為nil,這樣__block變量就不會持有person對象了,循環(huán)引用被打破,代碼如上,示意圖如下:(以前我們也驗證過,__block變量里的person指針就是我們在外面拿到的person指針)

總結(jié):不用想也知道ARC環(huán)境下使用__weak解決循環(huán)引用最好。
3. MRC如何解決循環(huán)引用
我們知道MRC不支持使用__weak的,否則報錯“Cannot create __weak reference in file using manual reference counting”
所以,相對于ARC,MRC就只有兩種方式解決循環(huán)引用了。
① 使用__unsafe_unretained解決
int main(int argc, const char * argv[]) {
@autoreleasepool {
// MRC不支持__weak的否則報錯:??
// Cannot create __weak reference in file using manual reference counting
__unsafe_unretained MJPerson *person = [[MJPerson alloc] init];
person.age = 10;
person.block = [^{
NSLog(@"age is %d", person.age);
} copy];
[person release];
}
NSLog(@"111111111111");
return 0;
}
如上代碼,使用__unsafe_unretained修飾person,當(dāng)block引用person的時候,person的引用計數(shù)器不會增加,還是剛開始創(chuàng)建的時候的1,所以當(dāng)“ [person release];”person就掛了。
如果不使用__unsafe_unretained修飾,person創(chuàng)建的時候引用計數(shù)器為1,因為person默認(rèn)強(qiáng)指針,所以block引用person的時候又加1,所以當(dāng)“ [person release];”person引用計數(shù)器還是1,不會掛。
② 使用__block解決
還記得上面驗證的“注意”嗎?MRC環(huán)境,使用__block修飾對象類型,對象不會被retain,所以也可以解決循環(huán)引用。
代碼省略,可自行將上面的__unsafe_unretained替換成__block進(jìn)行驗證。
補(bǔ)充1:block屬性的建議寫法
MRC下block屬性的建議寫法
@property (copy, nonatomic) void (^block)(void);
ARC下block屬性的建議寫法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
- 使用copy會將棧上的block拷貝到堆上,如果不使用copy,block就不會拷貝到堆上,因為MRC編譯器不會自動copy,所以只能用copy。
- 以前我們說過,ARC環(huán)境,并且把block賦值給強(qiáng)指針,編譯器會自動把block拷貝到堆上,所以ARC使用copy和strong都可以,沒區(qū)別。
- 為了統(tǒng)一好記,我們統(tǒng)一對block使用copy。
為什么一定要copy到堆上呢?如果在棧上我們就無法控制block的生命周期,在堆上,什么時候讓它生讓它死都可以。
補(bǔ)充2:__strong typeof(weakSelf) myself = weakSelf;
為什么要這么寫:
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) myself = weakSelf;
//報錯:
//Dereferencing a __weak pointer is not allowed due to possible null value caused by race condition, assign it to strong variable first
NSLog(@"age is %d", myself->_age);
};
- 原因一,如果在block內(nèi)部通過weakSelf->_age會報錯“弱指針不允許訪問,因為有可能為null,讓你使用強(qiáng)指針”,所以我們就用__strong強(qiáng)指針來訪問self。
- 原因二,訪問self的時候我們使用一個臨時的強(qiáng)指針來訪問self,這樣在整個block執(zhí)行期間,可以保證self對象不會被銷毀,同時,block調(diào)用完后,臨時的強(qiáng)指針被銷毀,一切又回歸原來的樣子。這樣既能保證整個block執(zhí)行期間,self對象不會被銷毀,又能保證不會產(chǎn)生循環(huán)引用。
面試題:
block的原理是怎樣的?本質(zhì)是什么?
block是封裝了函數(shù)調(diào)用以及調(diào)用環(huán)境的OC對象,比如函數(shù)的調(diào)用地址、捕獲的變量都封裝到了里面。__block的作用是什么?有什么使用注意點?
編譯器會將__block變量包裝成一個對象,就是__Block_byref_person_0這種結(jié)構(gòu)體,可以解決block內(nèi)部無法修改auto變量的問題(自己思考為什么不能修改)。
使用注意:__block變量內(nèi)部自己也會進(jìn)行內(nèi)存管理,而且MRC環(huán)境下,__block修飾對象,對象不會被retain的。為什么使用__block修飾auto變量,在block內(nèi)部就能修改此變量了?
block內(nèi)部有個指針指向__Block_byref_person_0結(jié)構(gòu)體,通過訪問結(jié)構(gòu)體,再通過結(jié)構(gòu)體訪問變量進(jìn)行修改的。block的屬性修飾詞為什么是copy?使用block有哪些使用注意?
block一旦沒有進(jìn)行copy操作,就不會在堆上。使用注意:循環(huán)引用問題。block在修改NSMutableArray,需不需要添加__block?
NSMutableArray *array = [NSMutableArray array];
person.block = ^{
[array addObject:@"123"];
};
不需要。
如上代碼,這個我們以前說過,給array數(shù)組添加成員只是操作array,除非要把a(bǔ)rray指針指向其他地方才要使用__block(比如:array = nil)。
一般如果沒必要不要用__block修飾,因為還要包裝一層。
Demo地址:block循環(huán)引用