【iOS】——Block概要和使用规范

Block概要和使用规范

Block定义

Block是带有自动变量的匿名函数,这里的自动变量是局部变量,匿名函数是说不需要知道该函数的名称也可以调用。无需提前声明或命名就能作为参数传递给其他函数或方法,或者作为变量保存和执行

Block的语法

完整形式的Block语法和一般的C语言函数相比,只有两点不同。第一点是没有函数名,因为它是匿名函数。第二点是返回值类型前带有“^”(插入记号)记号。下面是Block语法格式:

^ 返回值类型 参数列表 表达式

^int (int count){return count + 1;}

Block语法可以省略返回值类型

^(int count){return count + 1;}

如果不使用参数,Block语法还可以省略参数列表

^void (void){printf("Blocks\n");}

在C语言中可以将函数地址赋值给函数指针的类型变量中,同样在Block语法下,可将Block语法赋值给Block类型的变量中。示例如下:

int (^blk)(int) = ^(int count) {return count + 1;};
int (^blk_t)(int);
blk_t = blk;

Block类型变量声明
返回参数 (^变量名称)(接受参数);

同时使用typedef重命名Block类型,因为Block类型变量和平时的使用类型相同,为了方便我们使用,我们通常都是用typedef来重命名Block。

typedef 返回参数 (^该Block的名称)(接收参数)

截获自动变量值

在定义Block的时候,其之前的变量都会被该Block所截获,该Block后接着再改变变量的值然后再调用,其Block中所捕获的值不会受到改变。

int main(int argc, const char * argv[]) {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;
}

_ _Block修饰符

自动变量截获只能保存执行Block语法瞬间的值并且保存后就不能修改这个值,如果要修改这个自动变量的值就需要在在声明时给这个自动变量附加_ _Block说明符。

int main(int argc, const char * argv[]) {__block int val = 10;__block const char *fmt = "val = %d\n";void (^blk)(void) = ^{printf(fmt, val);val = 20;};val = 2;fmt = "These values were changed. val = %d\n";blk();printf(fmt, val);return 0;
}
  • _ _block修饰符只能于局部变量,不能用全局变量或静态变量。

  • 在Block部使用__ block修的变量时,需要注意循环引用的问题如果Block被强引用并且同时引用了__ block修饰的变,可能会导致环引用,造成内存漏。为了避免这种情况,可以在Block内部使用weakSelf来弱引用self,或者使用__ weak饰符来修饰__ block变量。

  • 当__ block修饰的变量Block内部被修改,外部变量的值会被修改。这意味在Block执行完后,外部变量的值将保持被修改后的状态。

  • 在使用ARC(动引用计数)的情况下,__ block饰符会自动处理内存管理。但是,在非ARC环境下,你需要手动处理__block变量的内存管理,确保Block执行完毕后释放它们。

截获的自动变量

向Block截获的自动变量赋值会报错,如果调用变更该对象的方法则不会产生编译错误。
用OC中的NSMutableArray来说,截获它其实就是截获该类对象用的结构体实例指针,就是说你只要不改变其指针的指向和其指针的值,他就不会出错。

int main(int argc, const char * argv[]) {id array = [[NSMutableArray alloc] init];void (^blk)(void) = ^{id obj = [[NSObject alloc] init];[array addObject:obj];};blk();return 0;
}

如果这样写就改变了array的初始地址,所以他就会报错。

int main(int argc, const char * argv[]) {id array = [[NSMutableArray alloc] init];void (^blk)(void) = ^{array = [[NSMutableArray alloc] init];};blk();return 0;
}

还有需要注意的是在现在的Block中,截获自动变量的方法并没有实现对C语言数组的截获

block的属性修饰词是copy

因为Block的内存地址显示在栈区,栈区的特点就是创建的对象随时销毁,一旦销毁后续再次调用空对象就会造成程序崩溃。对Block进行copy操作之后,block存在堆区,所以在使用Block属性的时候Copy修饰。

block一旦没有进行copy操作,就不会在堆上。MRC 下 block 如果没有 copy 到堆上,值捕获不会对外部变量引用。 虽然 ARC 环境 strong 也可以修饰 Block,那是因为编译器会对 strong 修饰的 block 也会进行一次 copy 操作。

block在调用前需要进行判空

block的判空判断的是block变量有没有指向实际的Block对象

下面举个例子:

int (^blk)(int) = ^(int count) {return count + 1;};

判断blk这个变量有没有指向^(int count) {return count + 1;}这个代码块对象。

下面是一个block为空的例子:

int (^blk)(int) ;

可以看到blk并没有指向实际的Block对象,此时的block就是空的

当block为空的时候,CPU会去访问地址address=0x10,接着会报EXC_BAD_ACCESS错误。

Block的本质是一个结构体,其代码如下:

struct __block_impl {void *isa;         //类型int Flags;          //标识字段int Reserved;       //保留字段       void *FuncPtr;       //函数指针,这个会指向编译器给我们生成的下面的静态函数__main_block_func_0(实际执行的函数,也就是block中花括号里面的代码内容。)
};

0x10是十六进制,也就是struct基地址后的第16个字节,其中void *类型占8个字节,int类型占4个字节,所以0x10的地址就是FuncPtr的地址,而address=0x10的问题也正是对值为nil的block强行调用导致的。

调用Block的时候会去访问Block结构体中的FuncPtr指针,因为Block为空,所以此时的FuncPtr指针所指的位置是无效的,当访问这个无效的地址的时候就会报错

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用宝塔部署前后端分离springboot项目,以EasyPan为例

前置准备 服务器购买 请参考其他教程,这里不再赘述。 项目 部署到服务器前请确保项目在本地运行正常 安装宝塔面板 宝塔Linux面板的安装配置以及基本使用教程(超详细)_宝塔linux面板新手使用教程-CSDN博客 sql文件 IDEA中怎样导出数据…

删除的视频怎样才能恢复?详尽指南

在日常生活中,我们有时会不小心删除一些重要的视频文件,或者在整理存储空间时不慎丢失了珍贵的记忆片段。这时候,我们可以通过一些数据恢复工具和技巧,找回这些被删除的视频。本文将详细介绍几种常见且有效的视频恢复方法&#xf…

docker 安装jenkins详细步骤教程

Jenkins 是一个开源的持续集成(CI)和持续部署(CD)工具,用于自动化软件开发过程中的构建、测试和部署。 特点和功能: 持续集成:Jenkins 可以自动触发构建过程,检查代码变更并进行构建、测试和部署,以确保团队的代码始终保持可集成状态。 插件生态系统:Jenkins 拥有丰富…

Vue与ASP.NET Core Web Api设置localhost与本地ip地址皆可访问

Vue的设置 我们创建并启动一个Vue项目,如下所示: 打开cmd,输入ipconfig查询本地ip地址: 想通过本地ip地址访问,把localhost改成本地ip地址,发现打不开: 这是因为Vue项目默认只有localhost&…

【Linux】进程IO|重定向|缓冲区|dup2|dup|用户级缓冲区|模拟缓冲区

目录 前言 重定向 实验一 为什么log.txt文件的文件描述符是1 为什么向stdout打印的信息也出现在文件中 实验二 用户级缓冲区 为什么要有用户级缓冲区 系统调用 dup 为什么close(fd1)之后还能向log.txt写入数据? dup2 缓冲区 观察现象 测试1 测试2 测…

【Pytorch实战教程】Pytorch中保存和加载模型的详细介绍

文章目录 保存模型方法一:只保存模型参数方法二:保存整个模型加载模型方法一:加载模型参数(推荐)方法二:加载整个模型训练时保存模型加载并继续训练总结在PyTorch中,保存和加载模型是非常重要的步骤,尤其是在训练大规模模型或需要多次重复实验时。 保存模型 在PyTorch…

C++初阶学习第三弹——类与对象(上)

目录 一.初步认知对象 二.类的基本组成、 1.类的定义 2.类的访问限定符及封装 3.类的作用域 4、类的大小的计算 5.this指针 三.总结 一.初步认知对象 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。…

WPF脱机应用:实现拼音模糊搜索的AutoCompleteBox

引言 在用户界面设计中,提供快速且直观的搜索体验是提升应用可用性的关键。WPF(Windows Presentation Foundation)提供了丰富的控件,其中AutoCompleteBox是一个能够辅助用户输入并提供自动完成建议的控件。然而,在脱机…

Linux下普通用户无法执行sudo指令

当执行sudo指令时出现: xxx(普通用户名字) is not in the sudoers file 说明在/etc/sudoers文件中没有把xxx加入到可执行sudo指令的名单中,因此需要修改sudoers文件。 解决方法:1、vim /etc/sudoers (要…

Java类加载器实现机制详细笔记

1. 类加载器的基本概念 类加载器(ClassLoader):在Java中,类加载器负责将Java类动态加载到JVM中。它是实现动态类加载机制的核心组件,对于开发复杂应用程序(如插件系统、模块化设计等)至关重要。…

什么是hdfs如何使用驱动程序访问hdfs

目录 什么是hdfs 主要特点包括: 架构组成: 应用场景: 如何使用驱动程序访问hdfs 准备工作环境: 启动 Hadoop 服务 可能遇到的问题: ssh验证失败 验证Hadoop服务 对hdfs进行文件操作 什么是hdfs HDFS&#x…

vite5+vue3开发阅读APP实战笔记20240725

目前界面长成这样: 配置别名 修改vite.config.js import {defineConfig} from vite import vue from vitejs/plugin-vue import path from "path"// https://vitejs.dev/config/ export default defineConfig({server: {open: true,port: 8088,},plug…

论文阅读【检测】:商汤 ICLR2021 | Deformable DETR

文章目录 论文地址AbstractMotivation技术细节多尺度backbone特征MSDeformAttention 小结 论文地址 Deformable DETR 推荐视频:bilibili Abstract DETR消除对目标检测中许多手工设计的组件的需求,同时表现出良好的性能。然而,由于Transfor…

MongoDB - 聚合阶段 $count、$skip、$project

文章目录 1. $count 聚合阶段2. $skip 聚合阶段3. $project 聚合阶段1. 包含指定字段2. 排除_id字段3. 排除指定字段4. 不能同时指定包含字段和排除字段5. 排除嵌入式文档中的指定字段6. 包含嵌入式文档中的指定字段7. 添加新字段8. 重命名字段 1. $count 聚合阶段 计算匹配到…

Odoo 17 仪表盘开发指南:打造高效的数据可视化中心

在现代企业管理中,数据驱动的决策至关重要。Odoo 17 提供了强大的平台来构建自定义仪表板, 适用于数据统计、工作台、驾驶舱、数据可视化等场景,以便用户能够一目了然地监控关键指标并做出及时反应。本文将介绍如何在 Odoo 17 中开发一个灵活且高度定制化的仪表盘系统,包括…

12_TypeScript 模块 以及 模块化封装DB 库

TypeScript 模块 1、模块中暴露方法12、模块中暴露方法23、模块中暴露方法34、封装[上一节的db 库](https://blog.csdn.net/qq_46143850/article/details/140664100)5、TypeScript 命名空间 模块的概念(官方): 关于术语的一点说明&#xff1a…

TCP Nagle算法,TCP_CORK,延迟确认机制简单介绍

Nagle Nagle算法是一种改善TCP/IP网络效率的算法。 算法的目的 主要目的是减少网络中小数据包的数量,从而减少网络拥塞。它通过延迟发送小数据包来实现这一目标,直到有足够的数据可以发送一个完整的数据包。 工作原理 如果要发送的数据量达到了最大分段大小(Maximum Segmen…

Android笔试面试题AI答之Android系统与综合类(1)

答案仅供参考,来着文心一言、Kimi.ai 目录 1.简述嵌入式实时操作系统,Android 操作系统属于实时操作系统吗?嵌入式实时操作系统简述Android操作系统是否属于实时操作系统 2.简述Android系统的优势和不足?3.简述Android的系统架构 &#xff1…

实战深度学习--进行蘑菇分类

数据集:https://pan.quark.cn/s/4d3526600c0c 概述 本笔记将介绍如何使用Python和深度学习库(如TensorFlow和Keras)来构建一个卷积神经网络(CNN)模型,以区分可食用和有毒的蘑菇。我们将从数据准备、模型构…

Vue.js 与 Ajax(vue-resource)的集成应用

Vue.js 与 Ajax(vue-resource)的集成应用 Vue.js 是一款流行的前端JavaScript框架,以其简洁、灵活和高效的特点而受到开发者的喜爱。在实际开发中,与后端服务的通信是不可或缺的,而Ajax技术是实现这一功能的关键。在V…