iOS--第二章block

第二章block

  • blocks 概要
  • Blocks模式
    • Blocks语法
    • Blocks类型变量
    • 截获自动变量值
    • _block 说明符
  • Blocks的实现
    • Block的实质
    • 截获自动变量值
    • _block说明符
    • Block存储域

blocks 概要

Blocks是c语言的扩展,block是一个带有自动变量值的匿名函数,它也是一个数据类型;
或者说:带有自动变量的匿名函数;

  • 书上还提到了一大串源码例子,不过不是很懂,只知道它强调了Blocks提供了类似于C++和oc类生成实例或对象来保持变量值的方法,即Blocks保持自动变量的值;
  • 带有自动变量值的匿名函数这一概念并不仅指BLOCKs:
    在这里插入图片描述

Blocks模式

Blocks语法

语法类型如下:
^ 返回值类型 参数类型 表达式

void (^block1)()//无返回值,无参数
int (^block2)(int num1,int num2)//int类型返回值i,两个int类型参数

语法中要注意的几个点:

  • 省略返回值类型时,如果表达式中有return语句就使用返回值的类型,如果表达式中没有return语句就使用void类型。表达式中含有多个return语句时,所有return的返回值类型必须相同
  • 如果不使用参数,参数列表也可省略;
^void (void) {printf("Blocks\n");
  • 也可以返回值类型以及参数列表都省略

Blocks类型变量

在这里插入图片描述

这里强调了blocks可以是一种类型变量,后面我们还要学到blocks类型的对象 ;

声明Blocks类型变量的实例如下:

int (^block) (int) ;

Blocks类型变量与一般的c语言变量完全相同 ;可作为以下用于使用

  • 自动变量
  • 函数参数
  • 静态变量
  • 静态全局变量
  • 全局变量

因为和通常的变量相同,所以当然可以由Blockleixing变量向Block类型变量赋值 ;

int (^blk1) (int) = blk ;
int (^blk2) (int) ;
blk2 = blk1 ;

可以作为函数参数传递,也可以作为函数返回值返回:

void func (int (^blk) (int) )  {
int (^func () ) (int ) {
return ^ (int count ) {return count + 1;}//不过这个函数没太看懂,有点奇怪  

上面这几种计述方式非常复杂,应为blocks是一种数据类型,所以我们可以使用typedef解决该问题:

typedef int^blk_t ) (int) ;

上面的例子可改为:

void func (blk_t blk) {
blk_t func () {

同样的:可以通过Block类型变量调用Block和使用Block的指针类型变量 ;
其实可以总结为;Block类型变量可像c语言中其他类型变量一样使用

截获自动变量值

在Blocks中,Blocks可以截获表达式中那个使用的变量,即保存该变量的瞬间值。随意即使之后改变原来的自动变量,也不会改变Blocks所截获的自动变量的值 ;
这一概念的实质是Blocks中的变量会将截获的值存进一个结构体对象中 ;

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

_block 说明符

自动变量截获只能保存执行Block语法瞬间的值,保存后就不能修改其值,如果我们尝试修改其值 ;

int val = 0 ;
void (^blk) (void) = ^ (val = 1 ;) ;
blk ();
printf ("val = %d\n",val) ;

编译器此时会报错:
在这里插入图片描述

Blocks的实现

Block的实质

Block是“带有自动变量值的匿名函数:,但它实际上作为简单的c语言源代码处理的,通过支持Block的编译器,含有Block语法源代码转换为c语言源代码被编译 ;

int main () {
void (^blk) (void) = ^ {printf("Block\n";};
blk ()  ;
return 0 ;

可以转化为一下的代码:
在这里插入图片描述

交换后的源代码中:

static void _main_Block-func_0 (struct__main_block_imp1_0 * _cself)  {
printf ("Block\n") ;
}

这里根据Block语法所属的函数名和该Block语法在该函数出现的顺序值来给转换后的函数命名 ;
参数_cself 是——main_block_impl_0结构体的指针,

struct _main_block_impl_0 {
struct _block_impl impl ;
struct _main_block_desc_0 * Desc ;
}
struct _block_impl {
void* isa ;
int Flags ;
int Reserved ;
void* FuncPtr ;
} ;
struct _main_Block_desc_0 {
unsigned long reserved ;
unsigned long Block_size ;
};

从上面这些结构体来看,Block本身的实现就是依赖于这些结构体对象,这也能更好的理解之后入变量截获的实现等 ;

struct_main_block_impl_0 tmp = _main_block_impl_0 (_main_block_func_0, &_main_block_desc_0_DATA) ;
struct _main_block_impl_0* blk = &tmp ;

这段代码等同于

void^blk) (void) = ^ {printf("Block\n"); }

struct_main_block_impl_0 tmp = _main_block_impl_0 (_main_block_func_0, &_main_block_desc_0_DATA) ;
第一个参数是由Blcok语法转化的c语言函数指针,第二个参数是作为静态全局变量初始化的_main_block_desc_0 的结构体实例指针 ;这里意味着将Block语法生成的Block赋给Block类型变量blk ;

blk () ;
//变换后并去掉转换部分*blk->impl.FuncPtr) (blk) ;

这里_main_block_func_0 函数的指针被赋值成员变量FuncPtr中 ;
也说明了参数_cself指向Block值 ;

对于isa = &_NSConcreteStackBlock ;

这里要提到前面说过的;所谓Block就是oc对象 ;

在这里插入图片描述
上面所使用的objc_objectj结构体和objc_objc_class 结构体是在各对象和类的实现中使用的最基本的结构体 ;

对于下面

@interface Myobject : NSObject {
int val0 ;
int val1 ;
}
end

该类对象的结构体如下:

struct Myobject {
Class isa ;
int val0 ;
int val1 ;
} ;

即同过isa指针保持该类结构体实例指针 ;

在这里插入图片描述

这是再看上面的代码,_NSConcreteStackBlock相当于class_t结构体实例 ;在讲Block作为oc对象处理时,关于该类信息放置于_NSConcreteStackBlock中 ;

Block结构体:
在这里插入图片描述

截获自动变量值

书上的源码:
在这里插入图片描述
在这里插入图片描述

从上面的源码中,我们可以看到Block语法表达式中使用的自动变量被作为成员变量追加到咯_main_block_impl_0 结构体中 ;

struct _main_block_impl_0 {
struct _blcok_impl impl ;
struct _main_block_desc_0* Desc ;
const char* fmt ;
int val ;
} ;

Blcoks的自动变量截获只针对Block中使用的自动变量 ;

初始化该结构体的构造函数如下:
在这里插入图片描述

_main_block_impl_结构体实例的初始化如下:

在这里插入图片描述

也就是说 自动变量是在结构体中的成员变量中被截获保存其值 ;

总的来说,所谓“截获自动变量值”意味着在执行Block语法时,Block语法表达式所使用的自动变量值被保存到Block的结构体实例中 ;

对于之前说过的Blcok不能直接使用c语言数组类型的自动变量,可以看看下面构造函数:

void func (char a[10]) {
char b [10] = a ;
printf ("%d\n",b[0]) ;
}int main () {
char a[10] = {2} ;
func (a) ; 
}

将c语言数组类型变量赋值给c语言数组类型变量中
,这在c里时不允许的 ;

_block说明符

由上面可知,Block中所截获的自动变量仅仅截获自动变量的值。在Block结构体实例中重写该自动变量也不会改变原先可活的自动变量 ;
如果尝试修改就会引发编译错误 ;

但c语言中的

  • 静态变量
  • 静态全局变量
  • 全局变量
    在Block中任然可以使用,毕竟Block的本质还是c语言 ;
int global_val = 1 ;
static int static_global_val = 2 ;int main () {
static int static_val = 2 ;
void (^blk) (void) = ^ {
global_val * = 1 ;
static_global_val* = 2 ;
static_val *= 3 ;
};
return 0 ;
}

转换后源码如下
在这里插入图片描述

在这里插入图片描述

主要注意一下静态变量static_val的转换 ;
在结构体得1尘缘变量中保存的是static_val的指针 ;

除了上面的三种变量,还可以使用__block说明符,也可以说是__block存储类说明符,
c语言中的存储域类说明符 :

  • typedef
  • exterm
  • static
  • auto
  • register

__block说明符类似于static,auto,register说明符,用于指定将变量值设置到哪个存储域中。

使用了__block说明符后:

__block int val = 10 ;

这时,————block变量同Block一样变成__block_byref_val_0的结构体类型变量,也就是放在栈上的结构体实例,这个结构体初始化持有原自动变量的成员变量 ;
该结构体的声明如下:

struct __block_byref_val_0 {
void* __isa ;
__block_byref_val_0 * __forwarding ;
int __flags ;
int __size ;
int val ;
};
__block_byref_val_0 val = {
0,
&val,
0,
sizeof(__Block_byref_val_0),
10
};

当给__block变量赋值时

^{val = 1;}

其源代码转换如下:
在这里插入图片描述

这里的Block中的_main_block_impl_0结构体实例持有指向——block变量的__Block_byref_val_0结构体实例指针 ;同时通过其成员变量__forwarding访问成员变量val ;

在这里插入图片描述

Block存储域

Block转换为Block的结构体类型的自动变量, __block变量转换为block变量的结构体类型的自动变量,这些都是在栈上生成的该结构体的实例变量 ;

在这里插入图片描述

这里我们需要了解三个类:

  • _NSConcreteStackBlock
  • _NSconcreteGlobalBlock
  • _NSConcreteMallocBlock
    这三个类的区别在于它们的对象灰分配在不同的存储域中;
    在这里插入图片描述

之前的Block例子都是_NSConcretestackBlock类的
(关于Block对象的类型,我们可以看isa成员变量的赋值)

当在记述全局变量的地方使用Block语法时,生成的Block对象是NSConcreteGlobalBlock类对象

void (^blk) (void) = ^ {printf("Global Block\n") ;} ;
int main () {

从名字是那个就可以看出,这类block是可以全局调用的,和全局变量性质差不多 ;

除了上面这种情况,当Block语法的表达式中不使用应截获的自动变量时,这时的Block也为_NSConcreteGlobalBlock类对象 ;这时的block对象分配程序的数据域中 ;

配置在全局变量上的Block,可以在作用域外通过指针安全使用,但设置在栈上的Block,如果其所属的变量作用域结束,该Block就被废弃 ;为类解决这一问题,Blocks将Block和_block变量从栈上复制到堆上 ;这样即使作用域结束,堆上的Block任然可以存在 ,这类复制生成的Block对象的类为_NSConcreteMallocBlock ;

在这里插入图片描述

顺便注意一下,__block变量的结构体成员__forwarding可以实现无论变量分配在栈上还是堆上时都可以正确访问————block变量 ;

当ARC有效时,大多数情形下编译器都会恰当的进行判断,自动生成将Block从栈上复制到堆上的代码 ;

typedef int (^blk_t) (int) ;
blk_t func (int rate) {
return ^ (int count) (return rate* count;} ;

通过对应的ARC编译器转换之后如下:
在这里插入图片描述

这里的objc——retainBlock函数实际上就是_Block_copy函数 ;

书上把这个地方解释的挺清楚的:
在这里插入图片描述

像上面这种情况,将Block作为函数返回值返回时,编译器会自动生成复制到堆上的代码 ;

还有一些情况需要我们手动生成代码,将Block从栈上复制到堆上:
如向方法或函数的参数中传递Block时;
举个例子:在NSArray类的initWithObjects实例方法上传递Block时需要手动复制

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

该源代码在执行时会发生异常,需要修改一下:

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

从上面我们也可以知道BLock类型变量也可以调用copy方法 ;

不同类的Block对象调用copy时,复制的效果也不一样 ;

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2871721.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Lua面向对象

封装 实现了New方法,相当于创建了一个表obj,并设置元表可以通过obj表去访问__index指向表中的数据 继承 通过大G表传入字符串创建表,

安卓国产百度网盘与国外云盘软件onedrive对比

我更愿意使用国外软件公司的产品,而不是使用国内百度等制作的流氓软件。使用这些国产软件让我不放心,他们占用我的设备大量空间,在我的设备上推送运行各种无用的垃圾功能。瞒着我,做一些我不知道的事情。 百度网盘安装包大小&…

golang常用库之-golang常用库之-ladon包 | 基于策略的访问控制

文章目录 golang常用库之-ladon包 | 基于策略的访问控制概念使用策略 条件 Conditions自定义conditionLadon Condition使用示例 持久化访问控制(Warden) 结合 Gin 开发一个简易 ACL 接口参考 golang常用库之-ladon包 | 基于策略的访问控制 https://github.com/ory/ladon Lado…

Unity中UGUI中的PSD导入工具的原理和作用

先说一下PSD导入工具的作用,比如在和美术同事合作开发一个背包UI业务系统时,美术做好效果图后,程序在UGUI中制作好界面,美术说这个图差了2像素,那个图位置不对差了1像素,另外一个图大小不对等等一系列零碎的…

从零自制docker-4-【PID Namespace MOUNT Namespace】

文章目录 PID namespace代码mountnamespace通俗理解代码 PID namespace 每个命名空间都有独立的PID空间,即每个命名空间的进程都由一开始分配。 新建立的进程内部进程ID为1 代码 package main import ("log""os/exec""os""sy…

MySQL_数据库图形化界面软件_00000_00001

目录 NavicatSQLyogDBeaverMySQL Workbench可能出现的问题 Navicat 官网地址: 英文:https://www.navicat.com 中文:https://www.navicat.com.cn SQLyog 官网地址: 英文:https://webyog.com DBeaver 官网地址&…

Spring MVC文件上传配置

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 文件上传 Spring MVC文件上传基于Servlet 3.0实现;示例代码如下: Overrideprotected void customizeRegistration(ServletRegistration.Dynamic reg…

【开源鸿蒙】编译OpenHarmony轻量系统QEMU RISC-V版

文章目录 一、背景介绍二、准备OpenHarmony源代码三、准备hb命令3.1 安装hb命令3.2 检查hb命令 四、编译RISC-V架构的OpenHarmony轻量系统4.1 设置hb构建目标4.2 启动hb构建过程 五、问题解决5.1 hb set 报错问题解决 六、参考链接 开源鸿蒙坚果派,学习鸿蒙一起来&a…

AJAX概念和axios使用、URL、请求方法和数据提交、HTTP协议、接口、form-serialize插件

AJAX概念和axios使用 AJAX概念 AJAX就是使用XMLHttpRequest对象与服务器通信,它可以使用JSON、XML、HTML和text文本等格式发送和接收数据,AJAX最吸引人的就是它的异步特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,…

GPU密集型计算性能优化的方法和技术

对GPU密集型计算进行性能优化的方法和技术多种多样。通过一些优化策略和技术需要综合考虑应用程序的具体需求、所使用的GPU硬件、以及编程模型和库的选择。通过不断地分析和调整,可以实现GPU计算性能的持续提升。以下是一些常用的优化策略和技术: 算法优…

uni-popup(实现自定义弹窗提示、交互)

一般提示框的样式,一般由设计稿而定,如果用uniapp的showmodel,那个并不能满足我们需要的自定义样式,所以最好的方式是我们自己封装一个!(想什么样就什么样)! 一、页面效果 二、使用…

Redis的常用操作-String字符串类型

一、redis简介 redis 就是一个数据库,与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此redis 被应用于缓存方向。另外,redis 也经常用来做分布式锁。 二、redis作缓存,主要用来实现…

WebSocket多服务实例下的消息推送

最近在做一个项目,涉及到前后端的消息同步、推送,进而我们选择使用webSocket的方案进行实现,但是当websocket服务端部署在多个实例下,会出现前端socket意外断开导致无法收到消息的情况。手下我们先说我们的实现方案: 1…

【JavaScript】JavaScript 运算符 ⑤ ( 赋值运算符 | 基础赋值运算符 与 复合赋值运算符 )

文章目录 一、JavaScript 赋值运算符1、赋值运算符 概念2、基础赋值运算符 与 复合赋值运算符3、复合赋值运算符4、完整代码示例 一、JavaScript 赋值运算符 JavaScript 赋值运算符种类 : 基础赋值运算符 : 等于 : ; 复合赋值运算符 : 加等 : 减等 : -乘等 : *除等 : /取模等…

MongoDB的安装方法图文详细

官网:www.mongodb.com 选择 Products > Community Edition 就能进入社区版 在这里下载 windows 版对应的安装包 注意:6.0.1 版本的 MongoDB 配置环境变量有问题,并且我不知道怎么解决,如果想要避免出问题,建议使…

【javaWeb】在webapp中手动发布一个应用

标题 🐲一、为什么要在webapp中手动发布一个应用🎉二、手动发布步骤1.下载Tomcat2.解压并安装3.在webapps中创建文档 ✨三、总结 🐲一、为什么要在webapp中手动发布一个应用 好处解释灵活性手动发布应用程序可以根据自己的需求进行自定义配置…

《PyTorch 深度学习实战》- 第一章 深度学习回顾和PyTorch简介

《PyTorch 深度学习实战》- 第一章 深度学习回顾和PyTorch简介 1.1 PyTorch的历史 pytorch前身是Torch,Torch使用Lua和C语言,而后因为python的兴起,演变成为PyTorch。事实上,pytorch是提供动态图功能的chainer分支。 pytorch与…

【php基础】输出、变量、

php基础补充 1. 输出2.和"的区别3.变量3.1变量的命名规则3.2 两个对象指向同一个值3.3 可变变量 4.变量的作用域5. 检测变量 1. 输出 echo: 输出 print: 输出,输出成功返回1 print_r(): 输出数组 var_dump(): 输出数据的详细信息,带有数据类型和数…

将FastSAM中的TextPrompt迁移到MobileSAM中

本博文简单介绍了SAM、FastSAM与MobileSAM,主要关注于TextPrompt功能的使用。从性能上看MobileSAM是最实用的,但其没有提供TextPrompt功能,故而参考FastSAM中的实现,在MobileSAM中嵌入TextPrompt类。并将TextPrompt能力嵌入到MobileSAM官方项目提供的gradio.py部署代码中,…

字母异位词分组【每日一题】

可以通过案例找到规律&#xff0c;每个词排序完后是同一个&#xff0c;所以通过hasmap存储排序过的值做key&#xff0c;值是存储单词集合。 package HasTable;import java.util.*;class Solution {static List<List<String>> groupAnagrams(String[] strs) {Map&l…