2019独角兽企业重金招聘Python工程师标准>>>

使用clang的rewrite-objc filename 可以将有block的c代码转换成cpp代码。从中可以看到block的实现。

#include <stdio.h>
int main()
{void (^blk)(void) = ^{printf("Block\n");};blk();return 0;
}

使用clang rewrite-objc以后会看到block的实现

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

可以看到其实block是一个正常的OC类

来看看block是怎样访问外部变量的

int main()
{int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (^blk)(void) = ^{printf(fmt, val);};return 0;
}

转换之后,可以看到

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;const char *fmt;int val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
int main()
{int dmy = 256;int val = 10;const char *fmt = "val = %d\n";void (*blk)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);return 0;
}

block的变量会被复制进block中

如果当block要改变传入的变量值怎么办?首先看一下全局变量和本地静态变量

int global_val = 1;
static int static_global_val = 2;
int main()
{static int static_val = 3;void(^blk)(void) = ^{global_val *= 1;static_global_val *= 2;static_val *= 3;};blk();return 0;
}
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;int *static_val;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int *static_val = __cself->static_val; // bound by copyglobal_val *= 1;static_global_val *= 2;(*static_val) *= 3;}

由于全局变量是在Data Section中,所以直接可以访问。局部静态变量是通过将其指针传入到block中,block就可以对其值进行修改。

然后看一下__block修饰符变量

int main()
{__block int val = 10;void (^blk)(void) = ^{val = 1;};blk();return 0;
}
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__Block_byref_val_0 *val; // by ref__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {__Block_byref_val_0 *val = __cself->val; // bound by ref
(val->__forwarding->val) = 1;}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

__block修饰符的变量,会生成一个__Block_byref_val_0的struct,然后通过访问其__forwarding来访问val值。因为Block有可能是在stack或者heap中,所以用__forwarding来访问。之所以会将__block单独生成一个struct是因为可能该变量会被多个block使用。

Block分三种类型

0) NSConcreteStackBlock    --stack

1) NSConcreteGlobalBlock   --data area

2) NSConcreteMallocBlock   --heap

自动copy block

当开启ARC时,在某些情况编译器会自动copy block,从stack到heap。

typedef int (^blk_t)(int);
blk_t func(int rate)
{return ^(int count){return rate * count;};
}
blk_t func(int rate)
{blk_t tmp = &__func_block_impl_0(__func_block_func_0, &__func_block_desc_0_DATA, rate);tmp = objc_retainBlock(tmp);return objc_autoreleaseReturnValue(tmp);
}

有些情况,编译器是无法检测是否应该copy block:

当block作为参数传递到方法或函数中。

但是,如果该方法或函数在内部copy,就不用手动再copy:

0)cocoa framework方法, 有usingBlock

1) GCD API

- (id) getBlockArray
{int val = 10;return [[NSArray alloc] initWithObjects:[^{NSLog(@"blk0:%d", val);} copy],[^{NSLog(@"blk1:%d", val);} copy], nil];
}

__forwarding

当block从stack copy到 heap中时,block中用到的__block也会copy到heap中,并且copy到heap中的block拥有该__block。

__block int val = 0;
void (^blk)(void) = [^{++val;} copy];++val;blk();NSLog(@"%d", val);

在block和外的++val都会变成 ++(val.__forwarding->val);

当block copy到heap中后, stack中的__forwarding会指向heap中的__block, heap中的__forwarding会指向自己的__block值,这样保证了__forwarding指向的是同一个变量值。

Block什么时候会copy到heap中

0)对block调用copy方法。

1)block作为一个函数的返回值。 编译器自动copy

2)赋值给id或block type class 有__strong 修饰符的成员变量。   编译器自动copy

3)usingBlock, GCD API。  在函数内copy

什么时候用该copy block?

0) block是函数返回值

1) block赋值给id或block type class 有__strong 修饰符的成员变量。

2)3)usingBlock, GCD API。  在函数内copy

转载于:https://my.oschina.net/u/566401/blog/219568

iOS Block实现探究相关推荐

  1. iOS底层原理探究 第一探. 事件传递和响应者链

    一. 声明:  本文意在探讨, 也参考了几位大神的文章, 在最后我会把链接发出来, 如果有理解错误的地方, 请大神们指正哈! 二. 前言:  最近自己做项目的时候, 用到了UITabbarContro ...

  2. [iOS - Block基础再探究]

    文章目录 前言 1. Block简介 2. 语法 初始化和声明 3. Block类型变量 typedef 截获自动变量 --block 截获的自动变量 4.block的实现 4.1 Block的存储域 ...

  3. IOS block 教程

    2019独角兽企业重金招聘Python工程师标准>>> 本章学习目标: 1. 了解何谓block.  2. 了解block的使用方法. 注:变数=变量 Block 是iOS在4.0之 ...

  4. iOS之深入探究多线程实现、线程安全和线程死锁

    一.线程与进程 ① 线程与进程的定义 线程 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行: 进程要想执行任务,必须得有线程,进程至少要有一条线程: 程序启动会默认开启一条线程,这条线程被 ...

  5. iOS之深入探究CADisplayLink和NSTimer的对比和内存溢出问题

    CADisplayLink的基本说明和使用 一.什么是CADisplayLink? 简单地说,它就是一个定时器,每隔几毫秒刷新一次屏幕. CADisplayLink是一个能让我们以和屏幕刷新率相同的频 ...

  6. iOS - block变量捕获原理

    block对变量的捕获 1:可以捕获不可以修改变量 局部变量 2:可以捕获且可以修改变量 全局变量 静态变量 __block修饰的局部变量 原理分析: 1. 局部变量为什么可以被捕获确不能修改 int ...

  7. iOS底层原理探究-Runloop

    Runloop 1. 概述 一般来说,一个线程只能执行一个任务,执行完就会退出,如果我们需要一种机制,让线程能随时处理时间但并不退出,那么 RunLoop 就是这样的一个机制.Runloop是事件接收 ...

  8. iOS之深入探究动画渲染降帧

    一.为什么要对动画降帧? 众所周知,刷新频率越高体验越好,对于 iOS app 的刷新频率应该是越接近越 60fps 越好,主动给动画降帧,肯定会影响动画的体验.但是另一方面,我们也知道动画渲染的过程 ...

  9. iOS Block总结

    1. 作为属性而存在的Block testBlock.m文件里 @property (copy, nonatomic) void (^aBlock)();  // MRC下,block属性必须是显式标 ...

最新文章

  1. mysql连接,修改密码,增加用户,显示,导入导出
  2. python语言程序设计嵩天-Python语言程序设计基础(第2版)嵩天课后答案
  3. python连接sqlite加密_C#连接加密的Sqlite数据库的方法
  4. 2.1 Objective-C概述
  5. Linux+Tomcat+Jdk1.8+jenkins环境搭建
  6. Visual Studio 2008 中使用插件Extjs提示的方法
  7. c2000 汇编语言指令,C2000系CMD文件的配置理解
  8. 前端学习(3064):vue+element今日头条管理-状态管理
  9. Java 并发编程CyclicBarrier的应用与源码解析(基于ReentrantLock实现)
  10. ubuntu双系统时间同步_解决Ubuntu 16.04.6 + Win10 双系统时间错误且不一致问题
  11. 全球首发!计算机视觉Polygon Mesh Processing总结8——Remeshing Global Structure和Correspondences
  12. mysql php错误处理函数_PHP 错误处理
  13. 吴恩达深度学习tensorflow版本问题
  14. mysql至少选修了两门课程_数据库中用关系代数表达式,查询至少选修两门课程的学生的学号和姓名怎么写?...
  15. RecyclerView之利用ItemDecoration实现万能分割线
  16. p值>0.05,统计意义上不显著?
  17. TCP TIME_WAIT解决方案
  18. 聊聊 Apache、Tomcat 静态网页、动态网页
  19. hdfs-over-ftp使用说明
  20. Pandas个人最强笔记

热门文章

  1. 敏捷开发实践总结(二):关于测试
  2. 【分享几个日常巡检 监控数据库的语句】
  3. 去掉字符串两端的全角空格和半角空格(含源代码)
  4. React组件设计之边界划分原则
  5. JSON.stringify()
  6. 《虚拟化安全解决方案》一2.3 在Windows Server 2008上配置Microsoft Hyper-V
  7. 2015最流行的Android组件、工具、框架大全
  8. hdu 2795 段树--点更新
  9. 区块链热度背后的资本市场
  10. react 快速上手开发_React中测试驱动开发的快速指南