美文网首页
Block 知识点总结

Block 知识点总结

作者: 小木头 | 来源:发表于2018-12-27 20:44 被阅读5次

面试中经常会被问到什么是 Block, 谈谈你对 Block 的理解, 今天就做一个小的总结。

Block 定义

block 是将函数及其上下文封装起来的对象

首先, 定义一个简单的 AddBlock

  - (void)test1 {
    
    void(^AddBlock)(NSInteger a , NSInteger b) = ^(NSInteger a, NSInteger b) {
        NSLog(@"%ld",a+b);
    };
    AddBlock(3,4);
}

接着运行相关命令转换成 C++ 文件

clang -rewrite-objc TestBlock.m

__block_impl

struct __block_impl {
  void *isa;  // isa指针, 验证了 block 也是一个对象
  int Flags;
  int Reserved;
  void *FuncPtr; // 执行调用的函数指针
};

具体调用流程

  struct __TestBlock__test1_block_impl_0 {
  struct __block_impl impl;
  struct __TestBlock__test1_block_desc_0* Desc;
  __TestBlock__test1_block_impl_0(void *fp, struct __TestBlock__test1_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __TestBlock__test1_block_func_0(struct __TestBlock__test1_block_impl_0 *__cself, NSInteger a, NSInteger b) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7_1v6qx94n2gl27sx0j1dqlrs40000gn_T_TestBlock_54a84e_mi_0,a+b);
    }

static struct __TestBlock__test1_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __TestBlock__test1_block_desc_0_DATA = { 0, sizeof(struct __TestBlock__test1_block_impl_0)};

static void _I_TestBlock_test1(TestBlock * self, SEL _cmd) {

    void(*AddBlock)(NSInteger a , NSInteger b) = ((void (*)(NSInteger, NSInteger))&__TestBlock__test1_block_impl_0((void *)__TestBlock__test1_block_func_0, &__TestBlock__test1_block_desc_0_DATA));
    ((void (*)(__block_impl *, NSInteger, NSInteger))((__block_impl *)AddBlock)->FuncPtr)((__block_impl *)AddBlock, 3, 4);
}

最终会执行 __TestBlock__test1_block_impl_0 方法, 并把 isa 指针指向了 _NSConcreteStackBlock, desc 包含了 block 结构体大小等信息

截获变量

定义基本类型和对象相关的全局变量、静态全局变量、局部变量,局部静态变量

#import "TestBlock.h"


static int static_global_a; // 基本类型静态全局变量
int global_a; // 基本类型全局变量

static NSObject *static_global_object; // 静态全局对象
NSObject *global_object;    // 全局对象

@implementation TestBlock


- (void)test2 {
    static int static_inside_a; // 基本类型局部静态变量
    int inside_a; // 基本类型局部变量
    static NSObject *static_inside_object;// 静态局部对象
    NSObject *inside_object; // 局部对象
    
    static_global_a = 1;
    global_a = 2;
    static_inside_a = 3;
    inside_a = 4;
    
    static_global_object = [[NSObject alloc] init];
    global_object = [[NSObject alloc] init];
    static_inside_object = [[NSObject alloc] init];
    inside_object = [[NSObject alloc] init];
    
    void(^Block)(void) = ^(){
        NSLog(@"截获基本数据类型 %d,%d,%d,%d",static_global_a,global_a ,static_inside_a,inside_a);
        
        NSLog(@"截获对象 %@,%@,%@,%@",static_global_object,global_object ,static_inside_object,inside_object);
    };
    
    Block();
}
@end

转换后的实现如下

struct __TestBlock__test2_block_impl_0 {
  struct __block_impl impl;
  struct __TestBlock__test2_block_desc_0* Desc;
  int *static_inside_a;
  int inside_a;
  NSObject **static_inside_object;
  NSObject *inside_object;
  __TestBlock__test2_block_impl_0(void *fp, struct __TestBlock__test2_block_desc_0 *desc, 
                                  int *_static_inside_a, int _inside_a, 
                                  NSObject **_static_inside_object, NSObject *_inside_object, int flags=0) : static_inside_a(_static_inside_a), inside_a(_inside_a), static_inside_object(_static_inside_object), inside_object(_inside_object) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

可以发现 Block 只截获了 基本类型和对象的局部变量, 不同点是静态变量是以指针形式存在的, 并没有截获全局变量、静态全局变量, 对象类型的局部变量连同修饰权一起截获

类型 局部 静态局部 全局 静态全局
基本类型 ✔️ ✔️ ✖️ ✖️
对象 ✔️ ✔️ ✖️ ✖️

__block 修饰符

用简单的话来说, 如果要对截获的变量进行赋值操作, 需要添加 __block 修饰符

截获变量.png

用下面的例子看一下 __block 的具体实现

  - (void)test3 {
    __block int b = 2;
    void(^Block)(void) = ^(){
        b = 4;                  // __block 修饰后可以直接赋值
        NSLog(@"%d",b);
    };
    Block();
}

__block 修饰的对象转变成结构体 __Block_byref_b_0

  struct __Block_byref_b_0 {
  void *__isa;
__Block_byref_b_0 *__forwarding;
 int __flags;
 int __size;
 int b;
};

struct __TestBlock__test3_block_impl_0 {
  struct __block_impl impl;
  struct __TestBlock__test3_block_desc_0* Desc;
  __Block_byref_b_0 *b; // by ref
  __TestBlock__test3_block_impl_0(void *fp, struct __TestBlock__test3_block_desc_0 *desc, __Block_byref_b_0 *_b, int flags=0) : b(_b->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __TestBlock__test3_block_func_0(struct __TestBlock__test3_block_impl_0 *__cself) {
  __Block_byref_b_0 *b = __cself->b; // bound by ref

        (b->__forwarding->b) = 4;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7_1v6qx94n2gl27sx0j1dqlrs40000gn_T_TestBlock_b681a6_mi_2,(b->__forwarding->b));
    }

可以看到 __block 修饰的对象 b 重新赋值后, 底层转变为 b->__forwarding->b

__forwarding 作用

wa

__forwarding 存在的意义就是不论在任何位置, 都能够顺利访问同一个 __block 变量

Block 类型

  • _NSConcreteStackBlock
  • _NSConcreateGlobalBlock
  • _NSConcreateMallocBlock

MRC 情况下:

  • _NSConcreateGlobalBlock:未截获外部变量的情况
  • _NSConcreteStackBlock:引用外部变量,
  • _NSConcreateMallocBlock : [block copy] 或者截获__block 修饰的变量

ARC 情况下

  • _NSConcreateGlobalBlock : 未截获外部变量的情况
  • _NSConcreateMallocBlock : 变量在赋值的时候自动拷贝到堆区

内存管理

Block 内存管理.png

MRC 下, 栈上的 Block 随着作用域结束会被销毁, 堆上的 Block 随着作用域结束不会被释放

相关文章

  • iOS Block 部分一

    主要讲解 Block 的底层实现原理; Block部分一Block部分二Block部分三Block知识点总结 基础...

  • Block 知识点总结

    面试中经常会被问到什么是 Block, 谈谈你对 Block 的理解, 今天就做一个小的总结。 Block 定义 ...

  • iOS知识点(2)BLock

    总结 Block是一种特殊的数据类型,是每个iOS 开发者必须掌握的知识点,搜集了4篇高质量的博客,block基本...

  • Block常用知识点总结

    对于刚入门iOS的小菜鸟来说,block总显得那么晦涩难懂,即使一时了解了,开发中也总找不到使用的场景,甚至是不愿...

  • iOS 之 block(4.4)

    本章主题,讨论 block 的嵌套 总结:可以看出block 被捕获,有 BLOCK_FIELD_IS_BLOCK...

  • OC block 原理总结

    本文重点总结 OC block 的原理,并带上一些例子,不讨论 block 的写法和应用。 block 的本质总结...

  • SDWebImage4.0源码探究(二)具体代码拓展

    代码一 知识点:block参考:iOS中block的详解weakSelf、strongSelf <转自唐巧>Blo...

  • iOS底层原理总结 - 探寻block的本质(一)

    iOS底层原理总结 - 探寻block的本质(一) iOS底层原理总结 - 探寻block的本质(一)

  • Block 原理浅析

    Block 浅析 一、Block内存 (堆、栈、全局) 知识点: 栈区(stack)— 由编译器自动分配释放 ...

  • Block

    xx_cc iOS底层原理总结 - 探寻block的本质(一)iOS底层原理总结 - 探寻block的本质(二) ...

网友评论

      本文标题:Block 知识点总结

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