【block第四篇】实现
-------------------------------------------欢迎查看block连载博客【专栏】--------------------------------------
【block编程第一篇】block语法 【block编程第二篇】block捕获变量和对象。
【block编程第三篇】block的内存管理。 【block编程第四篇】block内部实现(当前)
【block编程第五篇】block中怎样避免循环引用
------------------------------------------------------------------------------------------------------------------------------
一、先看几道block相关的题目
这是一篇比較长的博文,前部分是block的測试题目,中间是block的语法、特性,block解说block内部实现和block存储位置。请读者耐心阅读。具备block基础的同学。直接调转到block的实现。
以下列出了五道题。看看是否能答对两三个。主要涉及block栈上、还是堆上、怎么捕获变量。
答案在博文最后一行
//-----------第一道题:--------------
void exampleA() {
char a = 'A';
^{ printf("%c\n", a);};
}
A.始终能够正常执行 B.仅仅有在使用ARC的情况下才干正常执行
C.不使用ARC才干正常执行 D.永远无法正常执行
//-----------第二道题:选项同第一题--------------
void exampleB_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{printf("%c\n", b);}];
}
void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
//-----------第三道题:选项同第一题--------------
void exampleC_addBlockToArray(NSMutableArray *array) {
[array addObject:^{printf("C\n");}];
}
void exampleC() {
NSMutableArray *array = [NSMutableArray array];
exampleC_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
//-----------第四道题:选项同第一题--------------
typedef void (^dBlock)();
dBlock exampleD_getBlock() {
char d = 'D';
return ^{printf("%c\n", d);};
}
void exampleD() {
exampleD_getBlock()();
}
//-----------第五道题:选项同第一题--------------
typedef void (^eBlock)();
eBlock exampleE_getBlock() {
char e = 'E';
void (^block)() = ^{printf("%c\n", e);};
return block;
}
void exampleE() {
eBlock block = exampleE_getBlock();
block();
}
【注】:以上题目摘自:CocoaChina论坛 tid=152222&toread=1">打开链接
二、block的定义
Block是C语言的扩充功能。
能够用一句话来表示Blocks的扩充功能:带有自己主动变量(局部变量)的匿名函数。
命名就是工作的本质。函数名、变量名、方法名、属性名、类名和框架名都必须具备。而能够编写不带名称的函数对程序猿来说相当有吸引力。
那么请求结果以何种方式通知调用者呢?一般是经过代理(delegate)可是,写delegate本身就是成本,我们须要写类、方法等等。
block提供了相似由C++和OC类生成实例或对象来保持变量值的方法。像这样使用block能够不声明C++和OC类,也没有使用静态变量、静态全局变量或全局变量,仅用编写C语言函数的源代码量就可以使用带有自己主动变量值的匿名函数。
三、block的实现
这里我们借住clang编译器的能力:具有转化为我们可读源代码的能力。
int main(){
void (^blk)(void) = ^{printf("block\n");};
blk();
return 0;
}
struct __block_impl{
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size
}__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
struct __main_block_impl_0{
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
}
static struct __main_block_func_0(struct __main_block_impl_0 *__cself)
{
printf("block\n");
}
int main(){
struct __main_block_impl_0 *blk = &__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);
(*blk->impl.FuncPtr)(blk);
}
__main_block_impl_0:block变量。
__main_block_func_0:尽管。block叫匿名函数。
可是,这个函数还是被编译器起了个名字。
__main_block_desc_0:block的描写叙述,注意,他有一个实例__main_block_desc_0_DATA
由于上面是C++的代码,能够将__main_block_impl_0的结构体总结一下,得到例如以下形式:
__main_block_impl_0{
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0 *Desc;
}
四、捕获自己主动变量值
int val = 10;
void (^blk)(void) = ^{printf("val=%d\n",val);};
val = 2;
blk();
上面这段代码,输出值是:val = 10.而不是2,这里查看【block第二篇】block捕获变量和对象。
__main_block_impl_0{
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0 *Desc;
int val;
}
int main(){
struct __main_block_impl_0 *blk = &__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA,val);
}
注意函数调用最后一个參数,即val參数。
static struct __main_block_func_0(struct __main_block_impl_0 *__cself)
{
printf("val=%d\n",__cself-val);
}
__block说明符
可是不能改动它,不然就是编译错误。
可是能够改变全局变量、静态变量、全局静态变量。
为了不给开发人员迷惑。干脆不让赋值。道理有点像:函数參数,要用指针,不然传递的是副本。
__block int val = 10;
void (^blk)(void) = ^{val = 1;};
struct __block_byref_val_0{
void *__isa;
__block_byref_val_0 *__forwarding;
int _flags;
int __size;
int val;
}
注意:__block_byref_val_0结构体中有自身的指针对象。难道要
之所以为啥要生成一个结构体,后面在具体讲讲。反正不能直接保存val的指针。由于val是栈上的,保存栈变量的指针非常危急。
五、block存储区域
typedef int (^blk_t)(int);
for(...){
blk_t blk = ^(int count) {return count;};
}
-(id) getBlockArray{
int val =10;
return [[NSArray alloc]initWithObjects:
^{NSLog(@"blk0:%d",val);},
^{NSLog(@"blk1:%d",val);},nil];
}
id obj = getBlockArray();
typedef void (^blk_t)(void);
blk_t blk = (blk_t)[obj objectAtIndex:0];
blk();
在ARC环境下,假设不确定是否要copy block尽管copy就可以。ARC会打扫战场。
输出一致说明是栈上,不一致说明是堆上。
typedef int (^blkt1)(void) ;
-(void) stackOrHeap{
__block int val =10;
int *valPtr = &val;//使用int的指针,来检測block究竟在栈上。还是堆上
blkt1 s= ^{
NSLog(@"val_block = %d",++val);
return val;};
s();
NSLog(@"valPointer = %d",*valPtr);
}
val_block = 11 valPointer = 11
调用copy之后的结果呢:
-(void) stackOrHeap{
__block int val =10;
int *valPtr = &val;//使用int的指针,来检測block究竟在栈上。还是堆上
blkt1 s= ^{
NSLog(@"val_block = %d",++val);
return val;};
blkt1 h = [s copy];
h();
NSLog(@"valPointer = %d",*valPtr);
}
在ARC下>>>>>>>>>>>无效果。 val_block = 11 valPointer = 10
__block变量存储区域
那么有了这个__forwarding指针。不管是栈上的block还是被拷贝到堆上,那么都会正确的訪问自己主动变量的值。
六、截获对象
编译器为了区分自己主动变量和对象,有一个类型来区分。
static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src){
_Block_objct_assign(&dst->val,src->val,BLOCK_FIELD_IS_BYREF);
}
static void __main_block_dispose_0(struct __main_block_impl_0 *src){
_block_object_dispose(src->val,BLOCK_FIELD_IS_BYREF);
}
__block修饰符可用于不论什么类型的自己主动变量
【__block循环引用】
依据上面讲的内容,block在持有对象的时候,对象假设持有block,会造成循环引用。解决的方法有两种:
1. 使用__weak修饰符。
id __weak obj = obj_
2. 使用__block修饰符。__block id tmp = self;然后在block中tmp = nil。这样就打破循环了。这个办法须要记得将tmp=nil。不推荐!
文章开头block測试题答案:ABABB
【block第四篇】实现的更多相关文章
- JDFS:一款分布式文件管理系统,第四篇(流式云存储续篇)
一 前言 本篇博客是JDFS系列博客的第四篇,从最初简单的上传.下载,到后来加入分布式功能,背后经历了大量的调试,尤其当实验的虚拟计算结点数目增加后,一些潜在的隐藏很深的bug就陆续爆发.在此之前笔者 ...
- Flask最强攻略 - 跟DragonFire学Flask - 第四篇 Flask 中的模板语言 Jinja2 及 render_template 的深度用法
是时候开始写个前端了,Flask中默认的模板语言是Jinja2 现在我们来一步一步的学习一下 Jinja2 捎带手把 render_template 中留下的疑问解决一下 首先我们要在后端定义几个字符 ...
- flask 第四篇 模板语言jinja2
是时候开始写个前端了,Flask中默认的模板语言是Jinja2 现在我们来一步一步的学习一下 Jinja2 捎带手把 render_template 中留下的疑问解决一下 首先我们要在后端定义几个字符 ...
- 从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点)
从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www ...
- 第四篇 Entity Framework Plus 之 Batch Operations
用 Entity Framework 进行 增,删,改.都是基于Model进行的,且Model都是有状态追踪的.这样Entity Framework才能正常增,删,改. 有时候,要根据某个字段,批量 ...
- 【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- 解剖SQLSERVER 第十四篇 Vardecimals 存储格式揭秘(译)
解剖SQLSERVER 第十四篇 Vardecimals 存储格式揭秘(译) http://improve.dk/how-are-vardecimals-stored/ 在这篇文章,我将深入研究 ...
- 解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)
解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译) http://improve.dk/parsing-dates-in-orcamdf/ 在SQLSERVER里面有几 ...
- 深入理解javascript作用域系列第四篇——块作用域
× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...
随机推荐
- COMMIT - 提交当前事务
SYNOPSIS COMMIT [ WORK | TRANSACTION ] DESCRIPTION 描述 COMMIT 提交当前事务. 所有事务的更改都将为其他事务可见,而且保证当崩溃发生时的可持续 ...
- windows测试物理网络
ping 192.168.10.88 -t ,参数-t是等待用户去中断测试
- tar (child): lbzip2: Cannot exec: No such file or directory tar (child): Error is not recoverable: exiting now tar: Child returned status 2 tar: Error is not recoverable: exiting now
tar解压bz2格式 报错 解决方法很简单,只要安装bzip2就行了,yum安装的命令如下: yum -y install bzip2 如果是无法联网,可以去官网下载安装包,进一步安装即可
- A、B、C、D四个字母,能组成多少个互不相同且无重复三位组合
package 第五天的作业;/* * A.B.C.D四个字母,能组成多少个互不相同且无重复三位组合 */public class Demo14 { public static void main(S ...
- socket编程(Java实现)
主要是前段时间学习的网络知识的基于TCP与UDP编程,Java实现简单的大小写字母的转化,该文主要参考: https://blog.csdn.net/yjp19871013/article/detai ...
- 《BUG创造队》第五次作业:项目需求分析改进与系统设计
项目 内容 这个作业属于哪个课程 2016级软件工程 这个作业的要求在哪里 实验九 团队作业5-团队项目需求改进与系统设计 团队名称 BUG创造队 作业学习目标 1.编写完整<软件需求规格说明书 ...
- 51nod 1175 区间第k大 整体二分
题意: 一个长度为N的整数序列,编号0 - N - 1.进行Q次查询,查询编号i至j的所有数中,第K大的数是多少. 分析: 仅仅就是一道整体二分的入门题而已,没听说过整体二分? 其实就是一个分治的函数 ...
- [GXOI/GZOI2019]与或和(单调栈)
想了想决定把这几题也随便水个解题报告... bzoj luogu 思路: 首先肯定得拆成二进制30位啊 此后每一位的就是个01矩阵 Q1就是全是1的矩阵个数 Q2就是总矩阵个数减去全是0的矩阵个数 ...
- Layui表格之多列合并展示
前言: 当我们在使用Layui的时候,有时表格中的列比较多,展示出来肯定是有问题的,这样就不得不舍弃一些列不展示,不展示是一种解决方案,但是更好的解决方案应该是合并展示. 这里的展示不是合并单元格,合 ...
- Go:struct
一.使用方式 方式3和方式4返回的是:结构体指针,编译器底层对 p.Name 做了转化 *(p).Name,方便程序员使用. type Person struct { Name string Age ...