問題:block內(nèi)部捕獲到的局部變量是局部變量的值,所以在block上修改不了局部變量抡驼。
但是局部的靜態(tài)變量捕獲是指針争占,所以block內(nèi)部可以修改值。全局變量和全局靜態(tài)變量沒有捕獲闷袒,可以直接獲取到(不存在作用域的問題)
怎么在block內(nèi)部修改局部變量坑律??囊骤?
方法一:改成靜態(tài)變量;卧瘛!也物!
方法二:__block修飾局部變量
void test(){
__block int a = 10;
MyBlock myBlock = ^{
a = 11;
NSLog(@"myBlock---------%d", a);//11
};
myBlock();
}
轉(zhuǎn)成底層代碼
typedef void(*MyBlock)(void);
//在block內(nèi)部捕獲的時(shí)候把&a傳進(jìn)來包裝成了__Block_byref_a_0對(duì)象
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __test_block_impl_0 {
struct __block_impl impl;
struct __test_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a) = 11;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_r0_rjxg_z6540vblvmqqv4klgsm0000gn_T_main_cd5b88_mi_0, (a->__forwarding->a));
}
static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __test_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*);
void (*dispose)(struct __test_block_impl_0*);
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0};
void test(){
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
MyBlock myBlock = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
可以看到__block修飾的局部變量a宫屠,在block內(nèi)部捕獲的時(shí)候 變成了一個(gè) __Block_byref_a_0 *a; // by ref 類型的對(duì)象 Blcok內(nèi)部有個(gè)指向這個(gè)對(duì)象的指針 而這個(gè)對(duì)象擁有a變量(而且這個(gè)對(duì)象擁有的內(nèi)存地址和我們聲明的a的內(nèi)存地址是一樣的) 被執(zhí)行Block的時(shí)候 通過被包裝對(duì)象的__forwarding對(duì)象 訪問到a 進(jìn)行修改
__block的內(nèi)存管理
當(dāng)block在棧上的時(shí)候 并不會(huì)對(duì)__block變量產(chǎn)生強(qiáng)引用(ARC)或者retain操作(MRC)
當(dāng)block被拷貝到堆上時(shí):
1.會(huì)調(diào)用Block內(nèi)部copy函數(shù) 這個(gè)函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
2._Block_object_assign函數(shù)會(huì)對(duì)__block變量形成強(qiáng)引用(retain) (__block修飾的變量也會(huì)被拷貝到堆上)
3.只要是被__block修飾的變量 copy后都是強(qiáng)引用 如果沒被__block修飾的對(duì)象 會(huì)根據(jù)對(duì)象的修飾符來確定是強(qiáng)引用還是弱引用
當(dāng)Block從堆中移除的時(shí)候:
1.會(huì)調(diào)用Block內(nèi)部的dispose函數(shù) 這個(gè)函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
2._Block_object_dispose 這個(gè)函數(shù)會(huì)斷開對(duì)__block修飾的變量的強(qiáng)引用(realse)
block內(nèi)部使用了被__block修飾的對(duì)象的話 那么block內(nèi)部的 Desc 描述指針也會(huì)多兩個(gè)函數(shù) 這兩個(gè)函數(shù)就是用于內(nèi)存管理的copy函數(shù)和dispose函數(shù)
不使用__block修飾對(duì)象類型
void test(){
NSObject *objc = [NSObject new];
MyBlock myBlock = ^{
NSLog(@"myBlock---------%@", objc);//11
};
myBlock();
}
typedef void(*MyBlock)(void);
struct __test_block_impl_0 {
struct __block_impl impl;
struct __test_block_desc_0* Desc;
NSObject *objc;
__test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, NSObject *_objc, int flags=0) : objc(_objc) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
NSObject *objc = __cself->objc; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_r0_rjxg_z6540vblvmqqv4klgsm0000gn_T_main_b0ec24_mi_0, objc);
}
static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->objc, (void*)src->objc, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->objc, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __test_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*);
void (*dispose)(struct __test_block_impl_0*);
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0};
void test(){
NSObject *objc = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
MyBlock myBlock = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, objc, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
__block修飾對(duì)象類型
void test(){
__block NSObject *objc = [NSObject new];
MyBlock myBlock = ^{
NSLog(@"myBlock---------%@", objc);//11
};
myBlock();
}
轉(zhuǎn)成底層代碼
typedef void(*MyBlock)(void);
struct __Block_byref_objc_0 {
void *__isa;
__Block_byref_objc_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *objc;
};
struct __test_block_impl_0 {
struct __block_impl impl;
struct __test_block_desc_0* Desc;
__Block_byref_objc_0 *objc; // by ref
__test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, __Block_byref_objc_0 *_objc, int flags=0) : objc(_objc->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
__Block_byref_objc_0 *objc = __cself->objc; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_r0_rjxg_z6540vblvmqqv4klgsm0000gn_T_main_c53779_mi_0, (objc->__forwarding->objc));
}
static void __test_block_copy_0(struct __test_block_impl_0*dst, struct __test_block_impl_0*src) {_Block_object_assign((void*)&dst->objc, (void*)src->objc, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __test_block_dispose_0(struct __test_block_impl_0*src) {_Block_object_dispose((void*)src->objc, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __test_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __test_block_impl_0*, struct __test_block_impl_0*);
void (*dispose)(struct __test_block_impl_0*);
} __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0), __test_block_copy_0, __test_block_dispose_0};
void test(){
__attribute__((__blocks__(byref))) __Block_byref_objc_0 objc = {(void*)0,(__Block_byref_objc_0 *)&objc, 33554432, sizeof(__Block_byref_objc_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"))};
MyBlock myBlock = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, (__Block_byref_objc_0 *)&objc, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
可以看到和__block修飾的普通變量差不多 但是包裝成的對(duì)象多了兩個(gè)函數(shù) copy函數(shù)和dispose函數(shù) 這兩個(gè)和Block描述中的copy函數(shù)和dispose函數(shù)還不一樣,當(dāng)Block拷貝的到堆上的時(shí)候 被__block修飾的對(duì)象(objc)轉(zhuǎn)化的對(duì)象(__Block_byref_objc_0)也會(huì)被拷貝到堆上焦除,這個(gè)時(shí)候會(huì)觸發(fā)被包裝對(duì)象的copy函數(shù) 而這個(gè)函數(shù)會(huì)根據(jù)外部__block修飾對(duì)象(objc)的引用計(jì)數(shù)修飾符來決定對(duì)外部對(duì)象(objc)是強(qiáng)引用還是弱引用激况。當(dāng)Block從堆中移除的時(shí)候 會(huì)調(diào)用內(nèi)部的dispose函數(shù) 這個(gè)函數(shù)會(huì)斷開對(duì)轉(zhuǎn)化對(duì)象(__Block_byref_objc_0)的引用關(guān)系 如果計(jì)數(shù)為0 就直接釋放 釋放后會(huì)調(diào)用自己的dispose函數(shù) 對(duì)自己內(nèi)部對(duì)象(objc) 斷開引用關(guān)系 如果此時(shí)objc的引用計(jì)數(shù)為0 就會(huì)被釋放掉。(轉(zhuǎn)化對(duì)象持有的objc和我們外部聲明的objc對(duì)象有同一片內(nèi)存地址 是同一個(gè)對(duì)象)
注意:上面的分析 都是ARC的情況下膘魄。如果是MRC轉(zhuǎn)化對(duì)象(_Block_byref objc_0) 對(duì)外部對(duì)象(objc) 一直都是弱引用 不管是什么情況乌逐。這也是為什么在MRC下我們使用__block來解決循環(huán)引用的原因
Block與循環(huán)引用
1.在ARC的情況下 我們常常使用__weak __unsafe__unretained來解決
我們使用__weak 和 __unsafe__unretained 都有一個(gè)共同的作用 就是使Block持有的對(duì)象指向我們聲明的對(duì)象的指針式弱引用,那么我們外部指向?qū)嵗龑?duì)象的指針一旦被釋放 實(shí)例對(duì)象就會(huì)被釋放 那么Block就會(huì)被釋放 從而解決循環(huán)引用创葡。區(qū)別就是如果我們使用的是__weak 一旦我們的實(shí)例對(duì)象被釋放浙踢,block內(nèi)部持有的指向我們實(shí)例對(duì)象的指針會(huì)被指nil 不會(huì)產(chǎn)生野指針,但是__unsafe__unretained 不會(huì)有這一步操作 所有如果使用__unsafe__unretained block內(nèi)部持有的指向我們實(shí)例對(duì)象的指針會(huì)還會(huì)指向我們已經(jīng)釋放的實(shí)例對(duì)象的地址 產(chǎn)生野指針灿渴。這也是它不安全的原因洛波。所以我們一般使用__weak來解決循環(huán)引用
2.在MRC的情況下 我們使用__unsafe__unretained __block來解決 因?yàn)椴恢С謜eak指針
這兩個(gè)都可以使Block內(nèi)部的指向我們實(shí)例對(duì)象的指針進(jìn)行retain操作
1.Block的原理是怎樣的豌研?本質(zhì)是什么回怜?
Block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對(duì)象 本質(zhì)上也是一個(gè)OC對(duì)象
Block內(nèi)部包含了一個(gè)__block_imp1 這個(gè)指針內(nèi)部包含了一個(gè)isa指針(也證明了本質(zhì)是個(gè)對(duì)象) 和 block內(nèi)部封裝的代碼塊的執(zhí)行地址 以及捕獲的變量的指針
和一個(gè)__main_block_desc_0 里面包含了block的大小等 如果內(nèi)部使用了實(shí)例對(duì)象或者_(dá)_block修飾的變量 會(huì)包含一個(gè)copy函數(shù)和dispose函數(shù).
2.__block的作用是什么彬碱?有什么使用注意點(diǎn)橄镜?
被__block修飾的變量 在block內(nèi)部可以被改變晌纫,因?yàn)樵赽lock內(nèi)部被__block修飾的變量會(huì)被包裝成一個(gè)__Block_byref_xxx_0的對(duì)象岗屏,這個(gè)對(duì)象內(nèi)部有一個(gè)指向我們聲明的變量的指針 指向的地址和我們聲明的變量是同一個(gè)地址禽捆,修改的時(shí)候也是通過這個(gè)對(duì)象拿到指針做修改藐石。block內(nèi)部對(duì)這個(gè)對(duì)象是強(qiáng)引用的误续。而如果被修飾的變量是實(shí)例對(duì)象 那么這個(gè)對(duì)象內(nèi)部會(huì)添加一個(gè)copy函數(shù)和dispose函數(shù)吨悍,在被copy到堆上時(shí),這個(gè)對(duì)象的copy函數(shù)會(huì)根據(jù)被修飾實(shí)例對(duì)象的關(guān)鍵字來確定對(duì)實(shí)例對(duì)象是強(qiáng)引用還是弱引用蹋嵌。再block釋放的時(shí)候育瓜,會(huì)調(diào)用dispose函數(shù) 來斷開對(duì)實(shí)例對(duì)象的引用關(guān)系。需要注意的是__block只能修飾實(shí)例對(duì)象 還有一些內(nèi)存管理問題 比如說修飾實(shí)例對(duì)象且該實(shí)例對(duì)象擁有該Block 會(huì)造成循環(huán)引用栽烂。而且在MRC下使用__block修飾的實(shí)例對(duì)象 不會(huì)被__Block_byref_xxx_0的對(duì)象retain操作躏仇。對(duì)象可能會(huì)被提前釋放恋脚。
3.Block的屬性修飾詞為什么是copy? 使用Block有哪些注意?
如果block不被copy 就是存儲(chǔ)在椄铺空間上慧起,我們就控制不了block的生命周期,可能我們使用的時(shí)候已經(jīng)被釋放了或者我們使用的時(shí)候 其內(nèi)部捕獲的變量已經(jīng)釋放了 導(dǎo)致程序錯(cuò)誤册倒。而拷貝到堆上蚓挤,我們可以方便的控制其生命周期。雖然增加了管理內(nèi)存的一些成本驻子。但是可以減少錯(cuò)誤灿意。在ARC的情況下 如果有一個(gè)強(qiáng)引用指向Block 內(nèi)部也會(huì)copy到堆上。使用strong也行崇呵。但是我們習(xí)慣使用copy缤剧。 需要注意的是不能引起循環(huán)引用。我們可以使用__weak來避免這個(gè)問題
4.Block修改NSMutableArray 需不需要添加__block域慷?
不需要荒辕,如果是對(duì)數(shù)組進(jìn)行增刪改查 我們不需要對(duì)其添加__block 因?yàn)椴皇歉淖償?shù)組的指針指向 只是操作數(shù)組。添加__block反而會(huì)增加開銷犹褒。
Block的copy
_NSConcreteStackBlock copy 變成 _NSConcreteMallocBlock
_NSConcreteGlobalBlock copy 還是全局block 什么也不做
_NSConcreteMallocBlock copy 引用計(jì)數(shù)加1
在ARC環(huán)境下 編譯器會(huì)根據(jù)情況自動(dòng)將棧上的Block復(fù)制到堆上 相當(dāng)于對(duì)棧上的Block進(jìn)行copy操作抵窒。
1.Blcok 作為函數(shù)返回值的時(shí)候 會(huì)自動(dòng)copy
2.將Block賦值給__strong強(qiáng)指針的時(shí)候 也會(huì)自動(dòng)做copy操作
3.Block作為GCD的參數(shù)時(shí) 也會(huì)被copy到堆上
4.Foundation框架下 block作為參數(shù)且方法名含有usingBlock時(shí) 會(huì)被自動(dòng)copy