美文网首页iOS
iOS Block解析

iOS Block解析

作者: 霸_霸霸 | 来源:发表于2019-03-08 16:29 被阅读0次

Block可表示为 " 带有自动变量值的匿名函数 "

1. 为什么是匿名函数

在C语言中, 函数必须要有函数名, 如

void funcName(int a)  {
    printf("%d\n", a);
}

由上面的代码可见, 不知道函数名就无法调用函数.

但是Block就不一样, 不需要函数名

^ (int a) {
    printf("%d\n", a);
}

2. 为什么叫做 " 带有自动变量值 "

2.1" 带有自动变量值 " 在Block中表现为 " 截获自动变量值 "

看个例子

int a = 10;
void (^blk)() = ^ {
    printf("%d\n",a);
};
a ++;
blk();

输出结果: 10

为什么输出的结果是10, 而不是11? 这就是Block的截获自动变量值, 这么做的原因是: 防止a被释放后, Block中的a就没有值了; 将a的值 " 截获 " 下来, 这样无论Block在什么时候调用, 都不会出现a被释放的情况. 我们利用clang命令看看Block的底层实现

int main() {
    int a = 10;
    void (*blk)() = (__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a));
    a ++;
    ((*blk)->FuncPtr)(blk);
}

从最后一句((blk)->FuncPtr)(blk);看到, blk调用了FuncPtr函数, 那这个FuncPtr是什么? 看下面的代码

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

FuncPtr是来自fp, fp又是__main_block_impl_0的第一个参数, 而我们在main函数中看到, __main_block_impl_0的第一个参数是__main_block_func_0, 也就是说 FuncPtr就是__main_block_func_0, 接着我们看看__main_block_func_0是什么样的

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy

        printf("%d\n",a);
}

int a = __cself->a; // bound by copy 我们发现, 这里的a是拷贝过来的, 所以后面的a++对Block中的a没有影响

2.2 __block关键字

说到Block外的变量问题, 不得不说说__block关键字, 要想在Block中重新对Block外的变量赋值, 必须要在Block外的变量前加上__block, 这是为什么呢? 我们看个例子

int main() {
    __block int a = 10;
    void (^blk)() = ^ {
        printf("%d\n",a);
    };
    a ++;
    blk();
}

输出结果是: 11

还是通过clang来查看底层的代码, 我们发现, 此时的a不再是int类型了, 而是一个结构体__Block_byref_a_0

int main() {
    __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {0,&a, 0, sizeof(__Block_byref_a_0), 10};
    
    void (*blk)() = (&__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &a, 570425344));
    (a.__forwarding->a) ++;
    ((blk)->FuncPtr)(blk);
}

我们来看看这个结构体的信息

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

其中, __forwarding也是一个__Block_byref_a_0的结构体指针, 指向哪儿暂时不看; 最后有个int a, 记住这个 a !!!!!

看看main函数有什么变化

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __main_block_impl_0(void *fp, struct __main_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;
  }
};

原来的int a变成了 __Block_byref_a_0 *a; // by ref, block的构造函数中的a也变成了结构体指针

最后, 我们还是看看__main_block_func_0方法

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref

        printf("%d\n",(a->__forwarding->a));
    }

__Block_byref_a_0 *a = __cself->a; // bound by ref表明, 这里的a不再是bound by copy了, 而是bound by ref, 是引用, 所以我们在Block调用前执行a++是会引起Block中的a发生变化的;
这里的__forwarding应该是指向结构体a自己, 第一个a就是结构体a, 最后的a是 int a, 是具体的值

2.3 截获Objective-C对象

以NSMutableArray为例

    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"1", @"2", nil];
    void (^blk)(void) = ^ {
        [arr insertObject:@"3" atIndex:2];
    };
    blk();

这样是可以的, 只是对可变数组进行修改, 而不是重新赋值

下面我们看一下对Objective-c对象重新赋值

重新赋值
报错, 加上__block关键字后即可正常使用

相关文章

  • block系列文章总结

    iOS源码解析:Block的本质<一>iOS源码解析:Block的本质<二>Objective C block背后...

  • iOS源码解析:Block的本质<一>

    iOS源码解析:Block的本质<一> iOS源码解析:Block的本质<一>

  • iOS中Block的用法,举例,解析与底层原理(这可能是最详细的

    iOS中Block的用法,举例,解析与底层原理(这可能是最详细的Block解析)

  • iOS Block底层解析二

    一、__block 的解析 接上一篇《iOS Block底层解析一》,在block中我们要修改局部变量(自动局部变...

  • block

    iOS中Block的用法,举例,解析与底层原理(这可能是最详细的Block解析)[http://www.cocoa...

  • iOS-block相关

    本篇涵盖block的解析、应用等. 1.Block是什么?2.循环引用,看我就对了3.iOS中block技术小结4...

  • iOS Block解析

    Block可表示为 " 带有自动变量值的匿名函数 " 1. 为什么是匿名函数 在C语言中, 函数必须要有函数名, ...

  • iOS-2 Block

    block块 系列文章: iOS Block浅浅析 - 简书 iOS Block实现原理 iOS Block __...

  • iOS Block存储域及循环引用

    系列文章:iOS Block概念、语法及基本使用iOS Block实现原理iOS Block __block说明符...

  • Block底层原理分析

    iOSBlock底层原理解析 目录 Block底层解析什么是block?block编译转换结构block实际结构b...

网友评论

    本文标题:iOS Block解析

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