当前位置: 首页 > news >正文

Objective-C blocks 概要

1.block的使用

1.1什么是block?

Blocks是C语言的扩充功能:带有自动变量(局部变量)的匿名函数。

“带有自动变量”在Blocks中表现为“截取自动变量"
“匿名函数”就是“不带名称的函数”

块,封装了函数调用及调用环境的OC对象

  • block的声明
// 1.
@property (nonatomic, copy) void(^myBlock1)(void);
// 2.BlockType:类型别名
typedef void(^BlockType)(void);
@property (nonatomic, copy) BlockType myBlock2;
// 3.
// 返回值类型(^block变量名)(参数1类型,参数2类型,...)
void(^block)(void);
  • block的定义
    // ^返回值类型(参数1,参数2,...){};// 1.无返回值,无参数void(^block1)(void) = ^{};// 2.无返回值,有参数void(^block2)(int) = ^(int a){};// 3.有返回值,无参数(不管有没有返回值,定义的返回值类型都可以省略)int(^block3)(void) = ^int{return 3;};// 以上Block的定义也可以这样写:int(^block4)(void) = ^{return 3;};// 4.有返回值,有参数int(^block5)(int) = ^int(int a){return 3 * a;};
  • block的调用
    // 1.无返回值,无参数block1();// 2.有返回值,有参数int a = block5(2)

2.block的底层数据结构

通过Clang将以下的OC代码转化为C++代码

// Clang
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

//main.m
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {void(^block)(void) = ^{NSLog(@"我是 block");};block();}return 0;
}

转化为C++代码

//参数结构体
struct __main_block_impl_0 {struct __block_impl impl;// block 结构体struct __main_block_desc_0* Desc;// block 的描述对象
/*
block 的构造函数** 返回值:__main_block_impl_0 结构体** 参数一:__main_block_func_0 结构体** 参数二:__main_block_desc_0 结构体的地址** 参数三:flags 标识位
*/__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;// &_NSConcreteStackBlock 表示存储在栈上impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};//block 结构体
struct __block_impl {void *isa;//block 的类型int Flags;int Reserved;void *FuncPtr;// block的执行函数指针,指向__main_block_func_0
};//封装了 block 中的代码
//参数是__main_block_impl_0结构体的指针
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {NSLog((NSString *)&__NSConstantStringImpl__var_folders_yx_7jg_wdg128v45l4cn_1g265h0000gn_T_main_03dcda_mi_0);}static struct __main_block_desc_0 {size_t reserved;size_t Block_size;// block 所占的内存空间
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main(int argc, const char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
/*** void(^block)(void) = ^{NSLog(@"调用了block");};** 定义block的本质:** 调用__main_block_impl_0()构造函数** 并且给它传了两个参数 __main_block_func_0 和 &__main_block_desc_0_DATA** __main_block_func_0 封装了block里的代码** 拿到函数的返回值,再取返回值的地址 &__main_block_impl_0,** 把这个地址赋值给 block*/void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
/*** block();** 调用block的本质:** 通过 __main_block_impl_0 中的 __block_impl 中的 FuncPtr 拿到函数地址,直接调用*/    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);}return 0;
}

3.block的变量捕获机制

  • 对于全局变量,不会捕获到block内部,访问方为直接访问
  • 对于auto类型的局部变量,会捕获到block内部,block内部会自动生成一个成员变量,访问方式为值传递
  • 对于static类型的局部变量,会捕获到block内部,block内部会自动生成一个成员变量,访问方式为指针传递
  • 对于对象类型的局部变量,block会连同修饰符一起捕获

3.1 auto自动变量

将以下 OC 代码转换为 C++ 代码,并贴出部分变动代码

int main(int argc, const char * argv[]) {@autoreleasepool {int age = 10;void(^block)(void) = ^{NSLog(@"%d",age);};block();}return 0;
}
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;int age;//生成的变量__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int age = __cself->age; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_yx_7jg_wdg128v45l4cn_1g265h0000gn_T_main_40c716_mi_0,age);}

可以看出:

  • 在__main_block_impl_0结构体中会自动生成一个相同类型的age变量
  • 构造函数__main_block_impl_0中多出了一个age参数,用来捕获外部的变量

由于传递方式为值传递,所以我们在block外部修饰age变量时,不会影响到block中的age变量

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

3.2static类型的局部变量

将以下OC代码转化为C++代码,并贴出部分变动代码

    static int age = 10;void(^block)(void) = ^{NSLog(@"%d",age);};age = 20;block();// 20
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;int *age;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age, int flags=0) : age(_age) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int *age = __cself->age; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_yx_7jg_wdg128v45l4cn_1g265h0000gn_T_main_cb7943_mi_0,(*age));}

可以看出:

  • __main_block_impl_0结构体中生成了一个相同类型的age变量
  • __main_block_impl_0构造函数多了个参数,用来捕获外部的age变量的地址

由于传递方式是指针传递,所以修改局部变量age时,age的值会随之变化

3.3全局变量

将以下OC代码转化为C++代码,并贴出部分变动代码

int height = 10;
static int age = 20;
int main(int argc, const char * argv[]) {@autoreleasepool {void(^block)(void) = ^{NSLog(@"%d,%d",height,age);};block();}return 0;
}
int height = 10;
static int age = 20;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;}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {NSLog((NSString *)&__NSConstantStringImpl__var_folders_yx_7jg_wdg128v45l4cn_1g265h0000gn_T_main_7a340f_mi_0,height,age);}

可以看出:

  1. __main_block_impl_0结构体中,并没有自动生成age和height全局变量,也就是说没有将变量捕获到block内部

虽然block语法的匿名函数部分简单地变换为了C语言函数,但从这个变换的函数中访问静态全局变量/全局变量并没有任何改变,可直接使用。

但是静态变量的情况下,转换后的函数原本就设置在含有Block语法的函数外,所以无法从变量作用域访问

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

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

3.4 _block修饰的变量

3.4.1 _block的使用

默认情况下block是不能修改外面的auto变量,解决办法?

  • 变量用static修饰(原因:捕获static类型的局部变量是指针传递,可以访问到该变量的内存地址
  • 全局变量
  • _block(我们只是希望临时用一下这个变量临时改一下而已,而改为static变量和全局变量会一直在内存中)
3.4.2 _block修饰符
  • _block同于解决block内部无法修改auto变量值的问题;
  • _block不能修饰全局变量,静态变量;
  • 编译器会将_block变量包装成一个对象(struct __Block_byref_age_0byref:按地址传递));
  • 加_block修饰不会改变变量的性质,他还是auto变量;
  • 一般情况,对捕获变量进行赋值(赋值!=使用)操作需要添加_block修饰符,比如给数组添加或删除对象,就不用加_bolck修饰符;
  • 在 MRC 下使用 __block 修饰对象类型,在 block 内部不会对该对象进行 retain 操作,所以在 MRC 环境下可以通过 __block 解决循环引用的问题;

将以下 OC 代码转换为 C++ 代码,并贴出部分变动代码

int main(int argc, const char * argv[]) {@autoreleasepool {__block int age = 10;void(^block)(void) = ^{age = 20;NSLog(@"block-%d",age);};block();NSLog(@"%d",age);}return 0;
}
struct __Block_byref_age_0 {void *__isa;
__Block_byref_age_0 *__forwarding;//持有指向该实例自身的指针int __flags;int __size;int age;
};struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;__Block_byref_age_0 *age; // by ref__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__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_age_0 *age = __cself->age; // bound by ref(age->__forwarding->age) = 20;NSLog((NSString *)&__NSConstantStringImpl__var_folders_yx_7jg_wdg128v45l4cn_1g265h0000gn_T_main_75529b_mi_0,(age->__forwarding->age));}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

可以看出:

  • 编译器会将_block修饰的变量包装成一个__Block_byref_age_0结构体对象
  • 以上age = 20 的赋值过程:通过block的__main_block_func_0结构体实例中( __Block_byref_age_0)类型的age指针,找到 __Block_byref_age_0结构体的对象, __Block_byref_age_0结构体对象持有指向实例本身的__forwarding指针,通过成员变量_forwarding访问 __Block_byref_age_0结构体里的age变量,并将值改为20;

在这里插入图片描述

相关文章:

Objective-C blocks 概要

1.block的使用 1.1什么是block&#xff1f; Blocks是C语言的扩充功能&#xff1a;带有自动变量&#xff08;局部变量&#xff09;的匿名函数。 “带有自动变量”在Blocks中表现为“截取自动变量" “匿名函数”就是“不带名称的函数” 块&#xff0c;封装了函数调用及调用…...

Linux操作系统-07-Linux安装应用

一、使用rpm安装应用&#xff08;不推荐&#xff09; 先下载到本地&#xff0c;以.rpm文件名结尾&#xff0c;下载完成后&#xff0c;再安装 rpm -qa | grep mysql #查询当前系统是否有下载过mysql包 先上传mysql的rpm安装包到linux的opt目录 安装 rpm -ivh …...

DevOps实战:Docker、Kubernetes与Jenkins的完美融合

DevOps与容器化技术&#xff1a;Docker、Kubernetes和Jenkins 引言 在软件开发领域&#xff0c;DevOps文化和容器化技术已经成为当今最热门的话题之一。DevOps的目标是缩短开发和运维之间的距离&#xff0c;提高软件交付的速度和质量。而容器化技术&#xff0c;如Docker和Kub…...

Python面向对象——程序架构

需求 创建图形管理器 -记录多种图形(圆形、矩形.) --提供计算总面积的方法&#xff0c; 要求:增加新图形&#xff0c;不影响图形管理器 测试: 创建图形管理器&#xff0c;存储多个图形对象。 通过图形管理器&#xff0c;调用计算总面积方法 思路 ​​​​​​​ 代码 # ------…...

springboot单体项目链路日志跟踪及接口耗时

最近接触一个新的传统项目,在联调过程中,查看日志特别不方便,既无trackId,即无接口耗时,所以写了该博客。话不多说,直接上代码 1、实体类user package com.yk.domain;import lombok.Data;@Data public class User {private Long id;private String username;private St…...

力扣hot---岛屿数量

dfs思路&#xff1a; 首先通过两层for循环遍历每一个点&#xff0c;如果这个点为0或者2&#xff08;这个2是什么呢&#xff1f;是在遍历该点以及该点连成的这一片区域中&#xff0c;因为通过深度优先搜索&#xff0c;遍历该点就等于遍历这一片区域&#xff0c;遍历这篇区域中的…...

如何在Linux使用docker安装Plik并实现无公网ip上传下载内网存储的文件资源

文章目录 1. Docker部署Plik2. 本地访问Plik3. Linux安装Cpolar4. 配置Plik公网地址5. 远程访问Plik6. 固定Plik公网地址7. 固定地址访问Plik 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&#xff0c;风趣幽默&…...

Nginx反向代理详解

1. 什么是反向代理 反向代理是一种服务器代理的方式&#xff0c;它代理了客户端的请求并将请求转发给后端服务器&#xff0c;然后将后端服务器的响应返回给客户端。在这个过程中&#xff0c;客户端并不直接与后端服务器通信&#xff0c;而是通过反向代理服务器来实现请求转发和…...

【Android】 ClassLoader 知识点提炼

1.Java中的 ClassLoader 1.1 、ClassLoader的类型 Java 中的类加载器主要有两种类型&#xff0c;即系统类加载器和自定义类加载器。其中系统类 加载器包括3种&#xff0c;分别是 Bootstrap ClassLoader、Extensions ClassLoader 和 Application ClassLoader。 1.1.1.Bootstra…...

16. C++标准库

C标准库兼容C语言标准函数库&#xff0c;可以在C标准库中直接使用C语言标准函数库文件&#xff0c;同时C标准库增加了自己的源代码文件&#xff0c;新增文件使用C编写&#xff0c;多数代码放在std命名空间中&#xff0c;所以连接C标准库文件后还需要 using namespace std;。 【…...

JVM内存结构介绍

1. 什么是JVM 我们都知道在 Windows 系统上一个软件包装包是 exe 后缀的&#xff0c;而这个软件包在苹果的 Mac OSX 系统上是无法安装的。类似地&#xff0c;Mac OSX 系统上软件安装包则是 dmg 后缀&#xff0c;同样无法在 Windows 系统上安装。 Java 代码为什么可以在 Windows…...

Linux常见指令总结

ls&#xff1a;显示当前目录下文件列表 常用的命令行参数&#xff1a; -l 显示更多的文件属性 -a 显示所有的文件/目录&#xff08;包括隐藏的&#xff09; -d 只显示目录 ps&#xff1a;参数可以叠加使用。 例如&#xff1a;ls -la 显示所有文件…...

Day35-Linux网络管理5

Day35-Linux网络管理5 1. 网卡配置2. DNS客户端域名解析配置3. 给网卡配多个IP4. ip地址查看和设置4.1 ifconfig命令4.2 ip命令4.3 ip命令&#xff1a;查看和设置网络配置4.4 ip命令帮助 5. 路由5.1 路由功能分类&#xff1a;5.2 查看路由&#xff1a;5.3 路由表&#xff1a;5.…...

9个神奇免费AI编程助手,实现高效自动代码生成!

在AIGC技术工具快速发展的时代&#xff0c;对高效智能编程工具的需求和关注已达到空前的高度。本文将介绍9款免费且好用的AI编程助手工具。无论你是经验丰富的开发人员还是刚开始编程旅程的新手&#xff0c;这些AI代码软件都能帮助你提高项目开发的生产力、创造力和准确性&…...

Python 导入Excel三维坐标数据 生成三维曲面地形图(体) 5-3、线条平滑曲面且可通过面观察柱体变化(三)

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…...

【CSP】2022–09-3 防疫大数据 100分 STL大模拟 使用map优化索引 有坑得注意

2022–09-3 防疫大数据 STL大模拟 使用map优化索引 2022–09-3 防疫大数据 STL大模拟 使用map优化索引基本思路遇到的问题&#xff08;学到的东西&#xff09;感悟完整代码 2022–09-3 防疫大数据 STL大模拟 使用map优化索引 这题中规中矩&#xff0c;不算太难也不算太简单&am…...

【Linux基础(三)】信号

学习分享 1、信号的基本概念2、查看信号列表3、常见信号名称4、signal库函数5、发送信号kill6、kill - signal &#xff08;无参信号&#xff09;示例6.1、kill - signal (不可靠信号)示例6.2、kill - signal (可靠信号)示例 7、信号分类7.1、信号运行原理分类7.2、信号是否携带…...

GEE图像可视化常用函数

目录 图层操作Map.addLayer&#xff08;&#xff09;Map.centerObject&#xff08;&#xff09; 直方图ui.Chart.image.histogram&#xff08;&#xff09; 时间序列统计ui.Chart.image.series&#xff08;&#xff09;ui.Chart.image.seriesByRegion&#xff08;&#xff09; …...

c++基础语法

文章目录 前言命名空间命名空间的使用 缺省参数缺省参数的使用 函数重载函数重载的作用函数重载的使用函数重载原理 引用引用的使用引用的使用场景引用和指针 extern Cinlineauto范围fornullptr 前言 大家好我是jiantaoyab&#xff0c;这篇文章给大家带来的是c语言没有的一些特…...

【工作实践-07】uniapp关于单位rpx坑

问题&#xff1a;在浏览器页面退出登录按钮上“退出登录”字样消失&#xff0c;而在手机端页面正常;通过查看浏览器页面的HTML代码&#xff0c;发现有“退出登录”这几个字&#xff0c;只不过由于样式问题&#xff0c;这几个字被挤到看不见了。 样式代码中有一行为&#xff1a…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...