Block

作者: coder_my | 来源:发表于2018-12-16 19:39 被阅读0次

参考:
Apple Block 源码
Objective-C高级编程 iOS与OS X多线程和内存管理

Block与函数指针调用

//函数指针调用
static int count = 0;
int (*funcPtr)(int) = &func;
NSLog(@"C原因函数指针调用: %d", funcPtr(count));

//Block方式调用
int(^block)(int count) = ^(int count) {
    return ++ count;
};
NSLog(@"Block方式调用: %d", block(count));

Block的类型和继承链

//Block的类型和继承链
//以下代码在MRC环境下

//定义在全局区
void(^globalBlock1)(void) = ^{
    
};

void test1() {
    //未截获自动变量
    void(^globalBlock2)(void) = ^{
        
    };
    //截获自动变量(MRC下验证,ARC会执行copy)
    int a = 0;
    void(^stackBlock)(void) = ^{
        NSLog(@"a: %d", a);
    };
    //截获自动变量 调用copy
    void(^mallocBlock)(void) = [^{
        NSLog(@"a: %d", a);
    } copy];
    
    NSLog(@"globalBlock1: %@", globalBlock1);
    NSLog(@"globalBlock2: %@", globalBlock2);
    NSLog(@"stackBlock: %@", stackBlock);
    NSLog(@"mallocBlock: %@", mallocBlock);
    
    //继承链
    NSLog(@"------------------------------");
    
    NSLog(@"globalBlock1 superclass: %@", [globalBlock1 superclass]);
    NSLog(@"stackBlock superclass: %@", [[stackBlock class] superclass]);
    NSLog(@"mallocBlock superclass: %@", [[mallocBlock class] superclass]);
    
    NSLog(@"------------------------------");
    
    NSLog(@"globalBlock1 superclass superclass: %@", [[globalBlock1 superclass] superclass]);
    NSLog(@"stackBlock superclass superclass: %@", [[[stackBlock class] superclass] superclass]);
    NSLog(@"mallocBlock superclass superclass: %@", [[[mallocBlock class] superclass] superclass]);
    
    NSLog(@"------------------------------");
    
    NSLog(@"globalBlock1 superclass superclass superclass: %@", [[[globalBlock1 superclass] superclass] superclass]);
}

输出结果为:


Block的类型和继承链

Block截获变量

//Block截获变量
static int static_global_val = 0;//静态全局变量
int global_val = 0;//全局变量

void test2() {
//    int val = 0;//不能在block内部修改的变量
    static int static_val = 0;//静态变量
    __block int block_val = 0;//__block修饰的变量
    void(^block)(void) = ^{
        
        static_global_val ++;
        global_val ++;
        static_val ++;
        block_val ++;
        
        NSLog(@"static_global_val: %d", static_global_val);
        NSLog(@"global_val: %d", global_val);
        NSLog(@"static_val: %d", static_val);
        NSLog(@"block_val: %d", block_val);
    };
    block();
}

输入clang编译命令:clang -rewrite-objc main.m -o main.cpp
在main.cpp中关键代码如下:


static int static_global_val = 0;
int global_val = 0;

struct __Block_byref_block_val_0 {
  void *__isa;
__Block_byref_block_val_0 *__forwarding;
 int __flags;
 int __size;
 int block_val;
};

struct __test2_block_impl_0 {
  struct __block_impl impl;
  struct __test2_block_desc_0* Desc;
  int *static_val;
  __Block_byref_block_val_0 *block_val; // by ref
  __test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, int *_static_val, __Block_byref_block_val_0 *_block_val, int flags=0) : static_val(_static_val), block_val(_block_val->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __test2_block_func_0(struct __test2_block_impl_0 *__cself) {
  __Block_byref_block_val_0 *block_val = __cself->block_val; // bound by ref
  int *static_val = __cself->static_val; // bound by copy

        static_global_val ++;
        global_val ++;
        (*static_val) ++;
        (block_val->__forwarding->block_val) ++;

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_16, static_global_val);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_17, global_val);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_18, (*static_val));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_19, (block_val->__forwarding->block_val));
    }
static void __test2_block_copy_0(struct __test2_block_impl_0*dst, struct __test2_block_impl_0*src) {_Block_object_assign((void*)&dst->block_val, (void*)src->block_val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __test2_block_dispose_0(struct __test2_block_impl_0*src) {_Block_object_dispose((void*)src->block_val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __test2_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __test2_block_impl_0*, struct __test2_block_impl_0*);
  void (*dispose)(struct __test2_block_impl_0*);
} __test2_block_desc_0_DATA = { 0, sizeof(struct __test2_block_impl_0), __test2_block_copy_0, __test2_block_dispose_0};
void test2() {
    static int static_val = 0;

    __attribute__((__blocks__(byref))) __Block_byref_block_val_0 block_val = {(void*)0,(__Block_byref_block_val_0 *)&block_val, 0, sizeof(__Block_byref_block_val_0), 0};
    void(*block)(void) = ((void (*)())&__test2_block_impl_0((void *)__test2_block_func_0, &__test2_block_desc_0_DATA, &static_val, (__Block_byref_block_val_0 *)&block_val, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 


        test2();
    }
    return 0;
}
  1. static int static_global_val = 0;//静态全局变量
    int global_val = 0;//全局变量
    可以直接访问并修改
  2. static int static_val = 0;//静态变量
    通过指针访问(*static_val) ++;
  3. __block int block_val = 0;//__block修饰的变量
    通过(block_val->__forwarding->block_val) ++;

__block说明符

Block生成了如下结构体:

struct __test2_block_impl_0 {
  struct __block_impl impl;
  struct __test2_block_desc_0* Desc;
  int *static_val;
  __Block_byref_block_val_0 *block_val; // by ref
  __test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, int *_static_val, __Block_byref_block_val_0 *_block_val, int flags=0) : static_val(_static_val), block_val(_block_val->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__block修饰的变量编译后生成了如下结构体:

struct __Block_byref_block_val_0 {
  void *__isa;
__Block_byref_block_val_0 *__forwarding;
 int __flags;
 int __size;
 int block_val;
};

Block内部执行的操作为:

  1. __Block_byref_block_val_0 *block_val = __cself->block_val;
  2. (block_val->__forwarding->block_val) ++;
    总结:
    在给__block修饰的成员变量赋值时候,通过__Block_byref_block_val_0的指针__forwarding访问成员变量block_val;

Block存储域不同的区别

配置在全局变量上的Block,从变量作用域外也可以通过指针安全使用。但设置在栈上的Block,如果其所属的变量作用域结束,该Block就被废弃。由于__block变量也配置在栈上,同样地,如果其所属的变量作用域结束,则该__block变量也会被废弃。
Blocks提供了将Block和__blok变量从栈上复制到堆上的方法来解决这个问题。将配置在栈上的Block复制到堆上,这样即使Block语法记述的变量作用域结束,堆上的Block还可以继续存在。

从栈上复制到堆上的Block与__block变量
复制到堆上的Block将_NSConcreteMallocBlock类对象写入Block用结构体实例的成员变量isa。
impl.isa = &_NSConcreteMallocBlock
而__block变量用结构体变量成员__forwarding可以实现无论__block变量配置在栈上还是堆上时都能正确地访问__block变量。
复制__block变量
验证复制__block变量
//验证复制__block变量
void test3() {
    __block int a = 1;
    NSLog(@"a=%d  地址:%p", a, &a);
    void(^block)(void) = ^{
        a ++;
        NSLog(@"a=%d  地址:%p", a, &a);
    };
//    [block copy];
    block();
    NSLog(@"a=%d  地址:%p", a, &a);
}

输出结果:


不执行copy,在栈区
void test3() {
    __block int a = 1;
    NSLog(@"a=%d  地址:%p", a, &a);
    void(^block)(void) = ^{
        a ++;
        NSLog(@"a=%d  地址:%p", a, &a);
    };
    [block copy];
    block();
    NSLog(@"a=%d  地址:%p", a, &a);
}

输出结果:


执行copy,copy后在堆区

栈上的Block复制到堆上在什么时候:

  1. 调用Block的copy实例方法时
  2. Block作为函数返回值返回时
  3. 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  4. 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时

Block的引用和循环引用

被__block修饰的对象类型

  • 当__block变量在栈上时,不会对指向的对象产生强引用
  • 当__block变量被copy到堆时会调用__block变量内部的copy函数,copy函数内部会调,_Block_object_assign函数,_Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain);如果__block变量从堆上移除会调用__block变量内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放指向的对象(release)
Block从栈复制到堆时对__block变量产生的影响
在一个Block中使用__block变量
在多个Block中使用__block变量
Block的废弃和__block变量的释放
复制__block变量

解决循环引用

  1. 通过__block修饰符
__block TwoViewController *weakSelf = self;
self.block = ^{
    weakSelf.title = @"__block";
    weakSelf = nil;
};
self.block();//不调用引起循环引用
  1. 通过__unsafe_unretained修饰符
__unsafe_unretained TwoViewController *weakSelf = self;
self.block = ^{
    weakSelf.title = @"__unsafe_unretained";
};
self.block();
  1. 通过__weak修饰符(自动检查置nil安全方式)
__weak TwoViewController *weakSelf = self;
self.block = ^{
    weakSelf.title = @"__weak";
};
self.block();

相关文章

  • iOS开发之Block原理探究

    Block概述 Block本质 Block调用 Block分类 Block循环引用 Block原理探究 Block...

  • block的使用

    定义block 返回类型 (^block名称)(参数) = ^(){block内容}; 调用block block...

  • Block 02 - __block

    Block 02 - __block __block 的作用 __block 可以解决 Block 内部无法修改 ...

  • iOS面试之Block大全

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS面试之Block模块

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS Block

    Block的分类 Block有三种类型:全局Block,堆区Block,栈区Block 全局Block 当Bloc...

  • iOS block 为什么官方文档建议用 copy 修饰

    一、block 的三种类型block 三种类型:全局 block,堆 block、栈 block。全局 block...

  • iOS开发block是用copy修饰还是strong

    Block分为全局Block、堆Block和栈Block1、在定义block没有引用外部变量的时候,block为全...

  • block 初探

    全局block, 栈block, 堆block

  • Block

    一、Block本质 二、 BlocK截获变量 三、__block 修饰变量 四、Block内存管理 五、Block...

网友评论

      本文标题:Block

      本文链接:https://www.haomeiwen.com/subject/gyntkqtx.html