【Objective-C】浅析Block及其捕获机制

目录

    • Block的基本使用
      • Block的声明
      • Block的实现
      • Block的调用
    • Block作为形参使用
    • Block作为属性使用
      • 给Block起别名
      • Block的copy
    • Block的捕获机制
      • auto类型的局部变量
      • __block浅析
      • static类型的局部变量
      • 全局变量
    • 其他问题

Block的基本使用

什么是Block?

Block (块),封装了函数调用以及调用环境的 OC 对象,Objective-C闭包(可以在内部访问外部的值),相当于C语言的函数指针,把一个函数写在一个函数内部,而OC并没有函数(方法)嵌套这一语法

Block的声明

void(^blockName)();
int(^blockName2)(int a, int b, int c);

格式: 返回值 (^block名称)(形参列表)
^代表块的符号

Block的实现

  1. 无参数无返回值
void(^blockName)(void) = ^{};
  1. 有参数无返回值
void(^blockName)(int a, int b) = ^(int a, int b){};
  1. 无参数有返回值
int(^blockName)(void) = ^int{return 3;
};

实现部分的返回值可以省略,像这样:

int(^blockName)(void) = ^{return 3;
};
  1. 有参数有返回值
int(^blockName)(int a, int b) = ^int(int a, int b){return 3 + a * b;
};

实现部分的返回值int同样可以省略

Block的调用

//无参数无返回值
blockName();//有参数有返回值
int result = blockName(7, 12);

现在已声明的blockName代表一个块,那么调用这个块既可以通过blockName(7, 12);,也可以这样:

int result = ^(int a, int b) {return 3 + a + b;
}(7, 12);

Block作为形参使用

Block作为形式参数在方法中的声明与上述格式略有不同(块的名称在外面)

现在Jaxon类和Jacky类中分别实现以下方法:
Jaxon.h

- (void)askJackyForHelp: (void(^)(int num))blockName isOK: (void(^)(BOOL boolValue))completion;

Jaxon.m

- (void)askJackyForHelp:(void (^)(int))blockName isOK:(void (^)(BOOL))completion {blockName(3);//传入completion块的参数非1即0completion(arc4random() % 2);
}

Jacky.m

- (void)helpDoWith: (int)num {NSLog(@"帮忙做事%d次", num);
}

接下来在main函数中调用:

Jaxon* jaxon = [[Jaxon alloc] init];
[jaxon askJackyForHelp:^(int num) {Jacky* jacky = [[Jacky alloc] init];[jacky helpDoWith: num];} isOK:^(BOOL boolValue) {//成功和失败的概率各占一半if (boolValue) {NSLog(@"帮忙成功");} else {NSLog(@"帮忙失败");}}];

运行结果:

请添加图片描述

这样是不是可以起到代理的作用,Jaxon委托Jacky帮忙做事,Jaxon实现不了的委托Jacky实现,因此Block块也可以用于界面传值或其他需要使用代理模式的程序设计中

Block作为属性使用

给Block起别名

文章开头也提到了块其实也是一种对象,可以将ta理解为一种数据类型

那么也可以用typedef关键字给Block起别名,看以下示例:

typedef void(^Help)(int num);
typedef void(^Finish)(BOOL boolValue);

上面的方法也就可以这样声明:

- (void)askJackyForHelp:(Help)blockName isOK:(Finish)completion;

块的属性关键字一般需要是是copy

@interface Jaxon : NSObject//无别名
@property (nonatomic, copy)void(^helpBlock)(int num);
//有别名
//@property (nonatomic, copy)Help helpBlock;
- (void)askMyselfDo;@end@implementation Jaxon- (void)askMyselfDo {self.helpBlock(5);
}@end

main函数:

Jaxon* jaxon = [[Jaxon alloc] init];
jaxon.helpBlock = ^(int num) {NSLog(@"我自己做%@次", @(num));
};
[jaxon askMyselfDo];

运行结果:
请添加图片描述

Block的copy

关于copy关键字,编者也简单了解一下,底层原理以后再加以详细的剖析:

ARC 环境下,编译器会根据情况自动将上的 block 复制到上,比如以下几种情况: 手动调用 block 的copy`方法时;

  • block 作为函数返回值时(Masonry 框架中用很多);
  • 将 block 赋值给__strong指针时;
  • block 作为 Cocoa API 中方法名含有usingBlock的方法参数时;
  • block 作为 GCD API 的方法参数时。

block 作为属性的写法:
ARC下写strong或者copy都会对 block 进行强引用,都会自动将 block 从栈 copy 到堆上;
建议都写成copy,这样 MRC 和 ARC 下一致。

Block刚创建时存放在栈区,使用时copy到堆区

Block的捕获机制

为保证Block内部能正常访问到外部的变量,Block有一种变量捕获机制

auto类型的局部变量

auto变量:正常定义出来的变量默认都是auto类型,只是省略了

auto int age = 20;

auto类型的局部变量会被捕获到block块内部,访问方式为值传递

int age = 10;
NSLog(@"%d %p", age, &age);
void(^blockName)(void) = ^ {NSLog(@"%d %p", age, &age);
};
age = 20;
//可以打印出来,说明block块是可以访问到外部信息的
blockName();
NSLog(@"%d %p", age, &age);

请添加图片描述
根据运行结果可以得出以下两点:

  • auto类型的局部变量被捕获到block块内部时,block内部会自动生成一个相同的成员变量,用来存储这个变量的值,因此打印的block外部的age地址与内部age地址不一样
  • 由于值传递,修改外部age变量的值,不会影响到block内部的变量

__block浅析

Block内部只能调用外部变量,不能修改:

请添加图片描述

Block 默认情况下是使用被捕获的外部变量的只读拷贝,因此在 Block 内部无法直接修改外部变量的值

解决办法如下:

  • 变量用static修饰(原因:捕获static类型的局部变量是指针传递,可以访问到该变量的内存地址)
  • 全局变量
  • __block(我们只希望临时用一下这个变量临时改一下而已,而改为 static 变量和全局变量会一直在内存中)

当变量被__block修饰时,block可以修改外部全局变量:

__block int age = 10;
NSLog(@"%d %p", age, &age);
void(^blockName)(void) = ^ {age = 30;NSLog(@"%d %p", age, &age);
};
blockName();
NSLog(@"%d %p", age, &age);

请添加图片描述


static类型的局部变量

static类型的局部变量会被捕获到block内部,访问方式指针传递

static int age = 10;
NSLog(@"%d %p", age, &age);
void(^blockName)(void) = ^ {NSLog(@"%d %p", age, &age);
};
age = 20;
blockName();
NSLog(@"%d %p", age, &age);

请添加图片描述

  • static类型的局部变量被捕获到block内部时,block块内部会生成一个相同类型的指针,指向捕获到内部的age变量的地址
  • 由于指针传递,修改外部的age变量的值,会影响到block内部的age变量

全局变量

全局变量不会被捕获到block内部,访问方式为直接访问

int _age = 10;
static int _height = 175;int main(int argc, const char * argv[]) {@autoreleasepool {NSLog(@"%d %p", _age, &_age);void(^blockName)(void) = ^ {_age = 30;NSLog(@"%d %p", _age, &_age);};blockName();_age = 20;NSLog(@"%d %p", _age, &_age);}return 0;
}

请添加图片描述

其他问题

对于对象类型的局部变量,block会连同ta的所有权修饰符一起捕获

为什么局部变量需要捕获,全局变量不用捕获呢?

  • 作用域的原因,全局变量哪里都可以直接访问,所以不用捕获;
  • 局部变量,外部不能直接访问,所以需要捕获;
  • auto 类型的局部变量可能会销毁,其内存会消失,block 将来执行代码的时候不可能再去访问那块内存,所以捕获其值;
  • static 变量会一直保存在内存中, 所以捕获其地址即可

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

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

相关文章

2.2.C++项目:网络版五子棋对战之数据管理模块的设计

文章目录 一、数据管理模块实现(一)功能 二、设计(一)数据库设计(二)创建user_table类 一、数据管理模块实现 (一)功能 数据管理模块主要负责对于数据库中数据进行统一的增删改查管…

【快速解决】在vs2022中配置SFML图形库

目录 SFML 图形库的安装步骤如下: 1.下载 SFML 在 SFML 的官网(下载对应操作系统版本的 SFML)。​编辑 2.解压文件 将下载的压缩包解压至任意位置,得到类似如下的目录结构: 3.配置 VS 打开 Visual Studio&#xff…

人工智能、机器学习、深度学习的区别

人工智能涵盖范围最广,它包含了机器学习;而机器学习是人工智能的重要研究内容,它又包含了深度学习。 人工智能(AI) 人工智能是一门以计算机科学为基础,融合了数学、神经学、心理学、控制学等多个科目的交…

深度学习_3_实战_房价预测

梯度 实战 代码: # %matplotlib inline import random import torch import matplotlib.pyplot as plt # from d21 import torch as d21def synthetic_data(w, b, num_examples):"""生成 Y XW b 噪声。"""X torch.normal(0,…

Java EE-servlet API 三种主要的类

上述的代码如下: import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.i…

使用CMake构建一个简单的C++项目

文章目录 一. 构建一个简单的项目二. 构建过程1. 创建程序源文件2. 编写CMakeList.txt文件3. 构建项目并编译源代码 附件 一. 构建一个简单的项目 最基本的CMake项目是从单个源代码文件构建的可执行文件。对于像这样的简单项目,只需要一个包含三个命令的CMakeLists…

查看当前cmake版本支持哪些版本的Visual Studio

不同版本的的cmake对Visual Studio的版本支持不同,以下图示展示了如何查看当前安装的cmake支持哪些版本的Visual Studio。 1.打开cmake-gui 2.查看cmake支持哪些版本的Visual Studio

django基于Python的房价预测系统+爬虫+大屏可视化分析

欢迎大家点赞、收藏、关注、评论 文章目录 前言一、项目介绍二、开发环境三、功能需求分析1 数据采集功能设计2数据管理功能设计3爬虫功能需求分析4 数据可视化功能需求分析数据库表的设计 四、核心代码五、效果图六、文章目录 前言 房价是一个国家经济水平的重要体现&#xff…

正点原子嵌入式linux驱动开发——Linux并发与竞争

Linux是一个多任务操作系统,肯定会存在多个任务共同操作同一段内存或者设备的情况,多个任务甚至中断都能访问的资源叫做共享资源。在驱动开发中要注意对共享资源的保护,也就是要处理对共享资源的并发访问。在Linux驱动编写过程中对于并发控制…

2、Kafka 生产者

3.1 生产者消息发送流程 3.1.1 发送原理 在消息发送的过程中,涉及到了两个线程——main 线程和 Sender 线程。在 main 线程 中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给 RecordAccumulator, Sender 线程不断从 RecordAccumulator 中…

为什么短信验证码要设置有效期?

安全性:验证码的主要目的是为了验证用户的身份,防止恶意或未经授权的访问。如果验证码没有有效期,恶意用户或攻击者可以获取验证码后无限期地尝试使用它。通过设置有效期,可以限制验证码的生命周期,提高系统的安全性。…

跳跃游戏Ⅱ-----题解报告

题目:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 与Ⅰ不同的是,这次要求找出最小的跳跃次数。思路也很简单,在每一次跳跃之后都更新最远的跳跃距离。 举个列子: 输入:2,3,1,1,4 第一次…

看我为了水作业速通C++!

和java不太一样的一样的标题打个*&#xff0c;方便对比 基本架构* #include<iostream> using namespace std; int main() { system("pause"); return 0; } 打印* cout << "需要打印的内容" <<endl endl 是一个特殊的输出流控…

【Java基础面试三十八】、请介绍Java的异常接口

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;请介绍Java的异常接口 …

JAVA高级教程-Java Map(6)

目录 6、Map的使用 6、Map的使用 package Map01;import java.util.HashMap; import java.util.Map; import java.util.Set;/*** Map接口的使用*/ public class Demo01_HashMap {public static void main(String[] args) {Map<String,String> mapnew HashMap<>();ma…

Hadoop3教程(三十一):(生产调优篇)异构存储

文章目录 &#xff08;157&#xff09;异构存储概述概述异构存储的shell操作 &#xff08;158&#xff09;异构存储案例实操参考文献 &#xff08;157&#xff09;异构存储概述 概述 异构存储&#xff0c;也叫做冷热数据分离。其中&#xff0c;经常使用的数据被叫做是热数据&…

Android12之DRM架构(一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

JVM——JVM概述以及双亲委派机制

JVM探究 请你谈谈你对JVM的理解&#xff1f;Java8虚拟机和之前的有什么变化更新&#xff1f;什么是OOM&#xff0c;什么是栈溢出StackOverFlowError&#xff1f;怎么分析&#xff1f;JVM的常用调优参数有哪些&#xff1f;内存快照如何抓取&#xff1f;怎么分析Dump文件&#x…

【Java基础面试三十五】、谈谈你对面向接口编程的理解

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;谈谈你对面向接口编程的…

2022年亚太杯APMCM数学建模大赛B题高速列车的优化设计求解全过程文档及程序

2022年亚太杯APMCM数学建模大赛 B题 高速列车的优化设计 原题再现&#xff1a; 2022年4月12日&#xff0c;中国高铁复兴号CR450动车组在开放线上成功实现单车时速435公里&#xff0c;相对速度870公里&#xff0c;创造了高铁动车组列车穿越开放线和隧道速度的世界纪录。新一代…