Block相关

Block介绍

Block是将函数及其执行上下文封装起来的对象
block调用就是函数调用

截获变量

  1. 局部变量
    ① 对于基本数据类型的局部变量,直接截获其值;
    ② 对于对象类型的局部变量,连同其所有权修饰符一起截获。
  2. 静态局部变量
    ① 以指针形式截获(所以即使没有__block修饰符,也可以对其进行赋值操作)
  3. 全局变量、静态全局变量
    block不进行截获,因为可以直接使用

__block修饰符

  1. 什么时候需要使用__block修饰符
    一般情况下,对被截获变量进行赋值操作时需要添加__block修饰符;
    在对局部变量进行赋值时需要使用__block修饰符,在对静态局部变量、全局变量、静态全局变量进行赋值时,不需要__block;
  2. __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;                          // ③ 手动断环
}

你可能感兴趣的:(Block相关)