Block介绍
Block是将函数及其执行上下文封装起来的对象
block调用就是函数调用
截获变量
- 局部变量
① 对于基本数据类型
的局部变量,直接截获其值;
② 对于对象类型
的局部变量,连同其所有权修饰符
一起截获。- 静态局部变量
① 以指针形式截获(所以即使没有__block
修饰符,也可以对其进行赋值操作)- 全局变量、静态全局变量
①block
不进行截获,因为可以直接使用
__block修饰符
- 什么时候需要使用__block修饰符
一般情况下,对被截获变量进行赋值操作时需要添加__block
修饰符;
在对局部变量进行赋值时需要使用__block修饰符,在对静态局部变量、全局变量、静态全局变量进行赋值时,不需要__block;- __block修饰的变量在编译后变成了对象
- (void)method{ __block int multiplier = 6; int(^Block)(int) = ^int(int num){ return num*multiplier; }; multiplier = 4; NSLog(@"result is %d", Block(2)); }
输出结果为:8;
//编译后__block int multiplier转换成了对象 struct __Block_byref_multiplier_0 { void *__isa; __Block_byref_multiplier_0 *__forwarding; int __flags; int __size; int multiplier; }; static void _I_MCBlock_method(MCBlock * self, SEL _cmd) { __attribute__((__blocks__(byref))) __Block_byref_multiplier_0 multiplier = {(void*)0,(__Block_byref_multiplier_0 *)&multiplier, 0, sizeof(__Block_byref_multiplier_0), 6}; int(*Block)(int) = ((int (*)(int))&__MCBlock__method_block_impl_0((void *)__MCBlock__method_block_func_0, &__MCBlock__method_block_desc_0_DATA, (__Block_byref_multiplier_0 *)&multiplier, 570425344)); (multiplier.__forwarding->multiplier) = 4; NSLog((NSString *)&__NSConstantStringImpl__var_folders_v__7s9r415d0_q_jlxz5f9y1skh0000gn_T_MCBlock_1f6bbd_mi_0, ((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2)); }
block的内存管理
block类型:
① 全局类型Block_NSConcreteGlobalBlock
;
② 栈类型Block_NSConcreteStackBlock
;
③ 堆类型Block_NSConcreteMallocBlock
;
block各个类型在内存中的位置
block的Copy操作
对栈上的Block进行copy操作,会将其copy到堆上;
即block 在赋值给 id 类型或者 block 类型的成员变量时,block 会拷贝到堆上
block的Copy操作
栈上Block的销毁
栈上的变量和block随着作用域的结束,会被释放掉
栈上Block的销毁
栈上Block的Copy
栈上Block
经过copy
会将Block
和__block
变量都copy
到堆上
栈上Block
随着作用域的结束会被销毁,如果是在MRC
下,堆上的并不会销毁从而产生内存泄露
栈上Block的Copy
栈上__block变量的Copy
栈上的__block
变量经过copy
后,其__forwarding
指针会指向堆中的__block
变量
所以(multiplier.__forwarding->multiplier)=4;
不论是在栈上的Block中还是堆上Block中调用,修改的都是堆中__block变量;
__block变量的Copy
block 的循环引用
案例一:
@interface MCBlock() @property (nonatomic, strong) NSMutableArray *array; @property (nonatomic, copy) void (^blockCycle)(NSString *); @end - (void)blockCycleTest{ _array = [NSMutableArray arrayWithObject:@"block"]; _blockCycle = ^(NSString *str){ NSString *originStr = _array[0]; NSLog(@"%@_%@", str, originStr); }; _blockCycle(@"hello"); } #pragma mark - 通过查看编译后的代码不难发现,编译后的block对象捕获了self,而self又是强引用指针,所以会产生循环引用 struct __MCBlock__blockCycleTest_block_impl_0 { struct __block_impl impl; struct __MCBlock__blockCycleTest_block_desc_0* Desc; MCBlock *self; __MCBlock__blockCycleTest_block_impl_0(void *fp, struct >__MCBlock__blockCycleTest_block_desc_0 *desc, MCBlock *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void _I_MCBlock_blockCycleTest(MCBlock * self, SEL _cmd) { (*(NSMutableArray **)((char *)self + OBJC_IVAR_$_MCBlock$_array)) = ((NSMutableArray * _Nonnull (*)(id, SEL, ObjectType _Nonnull))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("arrayWithObject:"), (id _Nonnull)(NSString *)&__NSConstantStringImpl__var_folders_v__7s9r415d0_q_jlxz5f9y1skh0000gn_T_MCBlock_c6aaf8_mi_2); (*(void (**)(NSString *))((char *)self + OBJC_IVAR_$_MCBlock$_blockCycle)) = ((void (*)(NSString *))&__MCBlock__blockCycleTest_block_impl_0((void *)__MCBlock__blockCycleTest_block_func_0, &__MCBlock__blockCycleTest_block_desc_0_DATA, self, 570425344)); ((void (*)(__block_impl *, NSString *))((__block_impl *)(*(void (**)(NSString *))((char *)self + OBJC_IVAR_$_MCBlock$_blockCycle)))->FuncPtr)((__block_impl *)(*(void (**)(NSString *))((char *)self + OBJC_IVAR_$_MCBlock$_blockCycle)), (NSString *)&__NSConstantStringImpl__var_folders_v__7s9r415d0_q_jlxz5f9y1skh0000gn_T_MCBlock_c6aaf8_mi_4); }
针对上诉问题有两种解决方法:通过weakSelf或者weakArray来断环
__weak typeof(self) weakSelf = self;// ① //__weak NSArray *weakArray = _array;// ② _blockCycle = ^(NSString *str){ NSString *originStr = weakSelf.array[0]; //NSString *originStr = weakArray[0]; NSLog(@"%@_%@", str, originStr); };
案例二:
- (void)blockCycleTest{ _array = [NSMutableArray arrayWithObject:@"block"]; __block MCBlock *blockSelf = self; _blockCycle = ^(NSString *str){ NSString *originStr = blockSelf.array[0]; NSLog(@"%@_%@", str, originStr); }; _blockCycle(@"hello"); }
实际上与案例一一样,只不过多了个__block来混淆视听,block依旧强引用了self,解决方法有三种
_array = [NSMutableArray arrayWithObject:@"block"]; __weak __block MCBlock *blockSelf = self; // ① 加上__weak关键字 _blockCycle = ^(NSString *str){ NSString *originStr = blockSelf.array[0]; //blockSelf = nil; // ② 手动断环 有一个弊端就是如果block没有被调用,那么这个环也是解除不了的 NSLog(@"%@_%@", str, originStr); }; _blockCycle(@"hello"); //如果没有调用该block,方法②是无法断环的,依旧会导致内存泄露 //_blockCycle = nil; // ③ 手动断环 }