block介绍(三)揭开神秘面纱(上)

CNN:经典Backbone和Block MobileNets(2017,MobileNets: Efficient Convolutional Neural Networks for Mobile VisionApplications),主要创新点是使用了depthwise separable convolutions。 Input feature map: FxFxM ,Output feature map: GxGxN的问题。经典的方式是使用N个KxKxM的卷积核,即KxKxM的卷积核的每个channel与FxFxM的feature map. 阅读详情

上一篇我们总结了各个情况下,block及其引用到的内存位置情况。

接下来几篇,我们将剖析编译器转码以及运行时库源码来一探block的究竟。

 

block到底是什么

我们使用clang的rewrite-objc命令来获取转码后的代码。

 

1、block的底层实现

我们来看看最简单的一个block:

简单的block

图一

 这个block仅仅打印栈变量i和j的值,其被clang转码为:

简单block的转码

图二

首先是一个结构体__main_block_impl_0(从图二中的最后一行可以看到,block是一个指向__main_block_impl_0的指针,初始化后被类型强转为函数指针),其中包含的__block_impl是一个公共实现(学过c语言的同学都知道,__main_block_impl_0的这种写法表示其可以被类型强转为__block_impl类型):

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

 isa指针说明block可以成为一个objc 对象。

__main_block_impl_0的意思是main函数中的第0个block的implementation,这就是这个block的主体了。

 这个结构体的构造函数的参数:

  1. block实际执行代码所在的函数的指针,当block真正被执行时,实际上是调用了这个函数,其命名也是类似的方式。
  2. block的描述结构体,注意这个结构体声明结束时就创建了一个唯一的desc,这个desc包含了block的大小,以及复制和析构block时需要额外调用的函数。
  3. 接下来是block所引用到的变量们
  4. 最后是一个标记值,内部实现需要用到的。(我用计算器看了一下,570425344这个值等于1<<29,即BLOCK_HAS_DESCRIPTOR这个枚举值)

所以,我们可以看到:

  1. 为什么上一篇我们说j已经不是原来的j了,因为j是作为参数传入了block的构造函数,进行了值复制。
  2. 带有__block标记的变量会被取地址来传入构造函数,为修改其值奠定了基础

 

接下来是block执行函数__main_block_func_0:

其唯一的参数是__main_block_impl_0的指针,我们看到printf语句的数据来源都取自__cself这个指针,比较有意思的是i的取值方式(带有__block标记的变量i被转码为一个结构体),先取__forward指针,再取i,这为将i复制到堆中奠定了基础。

再下来是预定义好的两个复制/释放辅助函数,其作用后面会讲到。 

最后是block的描述信息结构体 __main_block_desc_0,其包含block的内存占用长度,已经复制/释放辅助函数的指针,其声明结束时,就创建了一个名为__main_block_desc_0_DATA的结构体,我们看它构造时传入的值,这个DATA结构体的作用就一目了然了:

长度用sizeof计算,辅助函数的指针分别为上面预定义的两个辅助函数。

注意,如果这个block没有使用到需要在block复制时进行copy/retian的变量,那么desc中不会有辅助函数

 

至此,一个block所有的部件我们都看齐全了,一个主体,一个真正的执行代码函数,一个描述信息(可能包含两个辅助函数)。

 

2、构造一个block

我们进入main函数:

图一中的第三行(block的声明),在图二中,转化为一个函数指针的声明,并且都没有被赋予初始值。

而图一中的最后一行(创建一个block),在图二中,成为了对__main_block_impl_0的构造函数的调用,传入的参数的意义上面我们已经讲过了。

所以构造一个block就是创建了__main_block_impl_0 这个c++类的实例。

 

3、调用一个block

调用一个block的写法很简单,与调用c语言函数的语法一样:

blk();

其转码后的语句:

((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

将blk这个函数指针类型强转为__block_impl类型,然后取其执行函数指针,然后将此指针类型强转为返回void*并接收一个__block_impl*的函数指针,最后调用这个函数,传入强转为__block_impl*类型的blk,

即调用了前述的函数__main_block_func_0

 

4、objective-c类成员函数中的block

源码如下:

- (void)of1
{
    OBJ1* oj = self;
    void (^oblk)(void) = ^{ printf("%d\n", oj.oi);};
    Block_copy(oblk);
}

这里我故意将self赋值给oj这个变量,是为了验证前一章提出的一个结论:无法通过简单的间接引用self来防止retain循环,要避免循环,我们需要__block标记(多谢楼下网友的提醒)

转码如下:

struct __OBJ1__of1_block_impl_0 {
  struct __block_impl impl;
  struct __OBJ1__of1_block_desc_0* Desc;
  OBJ1 *oj;
  __OBJ1__of1_block_impl_0(void *fp, struct __OBJ1__of1_block_desc_0 *desc, OBJ1 *_oj, int flags=0) : oj(_oj) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __OBJ1__of1_block_func_0(struct __OBJ1__of1_block_impl_0 *__cself) {
  OBJ1 *oj = __cself->oj; // bound by copy
 printf("%d\n", ((int (*)(id, SEL))(void *)objc_msgSend)((id)oj, sel_registerName("oi")));}

objc方法中的block与c中的block并无太多差别,只是一些标记值可能不同,为了标记其是objc方法中的blcok。

注意其构造函数的参数:OBJ1 *_oj

这个_oj在block复制到heap时,会被retain,而_oj与self根本就是相等的,所以,最终retain的就是self,所以如果当前实例持有了这个block,retain循环就形成了。

而一旦为其增加了__block标记:

- (void)of1
{
    __block OBJ1 *bSelf = self;
    ^{ printf("%d", bSelf.oi); };
}

其转码则变为:

//增加了如下行
struct __Block_byref_bSelf_0 {
  void *__isa;
__Block_byref_bSelf_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 OBJ1 *bSelf;
};
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

//声明处变为

    __block __Block_byref_bSelf_0 bSelf = {(void*)0,(__Block_byref_bSelf_0 *)&bSelf, 33554432, sizeof(__Block_byref_bSelf_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, self};

clang为我们的bSelf结构体创建了自己的copy/dispose辅助函数,33554432(即1<<25 BLOCK_HAS_COPY_DISPOSE)这个值告诉系统,我们的bSelf结构体具有copy/dispose辅助函数。

而131这个参数(二进制1000 0011,即BLOCK_FIELD_IS_OBJECT (3) |BLOCK_BYREF_CALLER(128))

中的BLOCK_BYREF_CALLER在内部实现中告诉系统不要进行retain或者copy,

也就是说,在 __block bSelf 被复制至heap上时,系统会发现有辅助函数,而辅助函数调用后,并不retain或者copy 其结构体内的bSelf。

这样就避免了循环retain。

 

小结:

当我们创建一个block,并调用之,编译器为我们做的事情如下:

1.创建block所有的部件代码:一个主体,一个真正的执行代码函数,一个描述信息(可能包含两个辅助函数)。

2.将我们的创建代码转码为block_impl的构造语句。

3.将我们的执行语句转码为对block的执行函数的调用。

 

下一篇我们将剖析runtime.c的源码,并理解block的堆栈转换。


WGS84坐标系-地心地固坐标系-东北天坐标系 工作中遇见个问题,就是ue4中,使用的坐标描述是使用东北天坐标系,因为如果经纬度只能表达到小数点后6位,这就造成有时间物体摆放位置不准确的问题。解决这个问题,就是把经纬度在转成ue里边能够使用的东北天坐标系。东北天坐标系又叫站心坐标系,这个站心可以是自己定义的... 阅读详情

相关推荐

小伙伴们-GO-带你揭开Linux的神秘面纱

小伙伴们-GO-带你揭开Linux的神秘面纱 带你了解Linux操作系统优秀特质,一探Linux系统-满血复活的启动过程。解刨Linux 骨架-文件系统与目录结构 讲解它硬盘存储的秘密知识

fyydlz的博客 5410

Eigne 坐标变换(二)

坐标系变换是右乘 坐标变换是左乘 Eigen::Matrix4d 表示 平移+ 旋转 变换

douzi949389的博客 1226

基于Codex平台与AI Agent技能开发,实现抖音爆款视频分析与脚本自动生成

大家好,我是专注于AI应用开发的技术博主。最近在探索如何利用AI工具自动化内容创作时,我发现了一个非常有趣的组合:通过Codex平台创建自定义的Skill(技能),来批量分析抖音平台的爆款视频,并基于分析结果自动生成新的带货视频脚本。这听起来像是内容团队的“黑科技”,但实现起来其实有清晰的路径可循。本文将为你完整拆解这个实战项目,从环境搭建、Skill编写、数据抓取到视频脚本生成,手把手教你打造自己的“AI内容助理”。无论你是想了解AI Agent的开发,还是对短视频自动化创作感兴趣,都能从本文中获得可直接

weixin_34110749的博客 644

ORB_SLAM3 IMU预积分PreintegrateIMU

这篇博文主要分享ORB_SLAM3中Tracking::PreintegrateIMU(),其主要包括几个部分: 获得两帧之间的IMU数据 中值积分 IMU状态更新 关于IMU的理论推导参考: ORB_SLAM3_IMU预积分理论推导(预积分项) ORB_SLAM3_IMU预积分理论推导(噪声分析) ORB_SLAM3_IMU预积分理论推导(更新) ORB_SLAM3_IMU预积分理论推导(残差) 1.获得两帧之间的IMU数据 主要是为了获取当前帧和前一帧之间的IMU数据,以便用于后续的IMU预积分

火柴的初心的博客 1233

保姆级教程:用Python+OpenCV实现相移法与格雷码三维重建(从生成条纹到点云生成)

本文详细介绍了使用Python和OpenCV实现相移法与格雷码三维重建的全流程,从条纹生成到点云计算。通过结构光技术,结合相移法和格雷码,实现高精度的三维重建,适用于无纹理物体表面的密集对应点生成。教程包含环境搭建、条纹生成、相位计算、三维点云重建及常见问题排查,适合计算机视觉开发者和研究人员。

weixin_33443333的博客 349

在不同版本Python中安装PyQT5及PyQT5-Tools

在不同版本Python中安装PyQT5及PyQT5-Tools

zoomgu680的博客 2万+

block(3) - 变量与__block修饰符

__block修饰变量的时候时候 1、__block修饰本地变量转为cpp的样子 //用__block修饰的本地变量b多了这个结构体 struct __Block_byref_b_0 { //直译为本地变量b在block中是通过引用的 void *__isa; __Block_byref_b_0 *__forwar...

swel的博客 587

rk3568 RGMII KSZ8795 MAC TO MAC

参考《Rockchip_Developer_Guide_Linux_MAC_TO_MAC_CN.pdf》

明天乌儿会歌唱 6491

iOS中block介绍(三)揭开神秘面纱(上)

上一篇我们总结了各个情况下,block及其引用到的内存位置情况。接下来几篇,我们将剖析编译器转码以及运行时库源码来一探block的究竟。 AD:51CTO 网+ 第十二期沙龙:大话数据之美_如何用数据驱动用户体验 block到底是什么 我们使用clang的rewrite-objc命令来获取转码后的代码。 1、block的底层实现 我们来看看最简

lijunhao2011的博客 526

无人机巡检提速3倍!YOLOv8n+OpenVINO让Jetson Nano跑25FPS,还能多飞25分钟

摘要(149字): 针对无人机巡检中Jetson Nano算力不足导致的帧率低、首次推理延迟高、续航短等问题,提出YOLOv8n+OpenVINO联合优化方案: 模型轻量化:YOLOv8n参数量仅3.2M(较v8s减少71%),Jetson Nano帧率从8FPS提升至15FPS(原生),绝缘子裂纹召回率仅降3%; OpenVINO加速:AUTO模式动态分配CPU/GPU算力,首次推理延迟从500ms降至200ms,帧率再优化至25FPS; 能效提升:功耗降低40%,单次续航延长25分钟。方案已落地电力/林

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1241

GAN网络详解析

介绍目前常见的GAN网络,以及GAN网络的基本组成

wm199的博客 6504

ios 添加block 类别_iOS中block介绍(三)揭开神秘面纱(上)

block到底是什么我们使用clang的rewrite-objc命令来获取转码后的代码。1、block的底层实现我们来看看最简单的一个block:这个block仅仅打印栈变量i和j的值,其被clang转码为:首先是一个结构体__main_block_impl_0(从图二中的***一行可以看到,block是一个指向__main_block_impl_0的指针,初始化后被类型强转为函数指针),其中包含...

weixin_39828859的博客 102

Joern实战:从代码导入到Python分析的全流程避坑指南(附常见错误解决方案)

本文详细介绍了Joern静态分析工具从代码导入到Python分析的全流程实践指南,包括环境配置、代码导入技巧、数据库服务优化、Python交互分析及性能调优等关键步骤。特别针对常见错误提供了实用解决方案,帮助开发者高效进行代码安全分析和漏洞挖掘。

weixin_28309571的博客 138

TPS259483AYWPR与STM32F405RG工业电源协同设计实战指南

电子保险丝(eFuse)是现代嵌入式系统物理层安全的核心器件,其原理基于高精度电流检测与纳秒级硬件响应,技术价值在于绕过软件延迟实现真正实时保护。在工业场景中,它需应对电机启停、浪涌冲击、宽温运行等严苛工况,而实际性能高度依赖与MCU(如STM32F405RG)的深度协同——包括I²C动态配置、FAULT信号电气鲁棒性设计及自适应限流策略。本文聚焦TPS259483AYWPR在真实工业环境中的落地约束,涵盖PCB布局寄生效应、RSENSE热漂移、低温Rds(on)补偿、故障诊断状态机等关键实践,为通过IEC

weixin_34375251的博客 315

【3天快速入门WPF】1-从最简单的xaml开始

首先我们要知道xaml语法中声明即定义,意思就是“见到元素就相当于创建实例”,MainWindow.xaml的主干是一个元素里包含着一个 元素。这段程序就是在定义一个Window类的实例,这个实例的一个组成部分是一个Grid类的实例。

Zy100Papa的博客 1622

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

iOS 开发,block是什么?clang -rewrite-objc反编译block,带你揭开block的神秘面纱

iOS 开发,block是什么?clang -rewrite-objc反编译block,带你揭开block的神秘面纱

horisea的博客 2262

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Block介绍(三)解开神秘面纱(上)

上一篇我们总结了各个情况下,block及其引用到的内存位置情况。 接下来几篇,我们将剖析编译器转码以及运行时库源码来一探block的究竟。   block到底是什么 我们使用clang的rewrite-objc命令来获取转码后的代码。   1、block的底层实现 我们来看看最简单的一个block: [caption id="attachment

技术之窗 665

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

block的理解 ios_iOS中block介绍(四)揭开神秘面纱(下)

看此篇时,请大家同时打开两个网址(或者下载它们到本地然后打开):内存管理的真面目objc层面如何区分不同内存区的blockBlock_private.h中有这样一组值:/*therawdataspaceforruntimeclassesforblocks*//*class+metausedforstack,malloc,andcollectablebasedb...

weixin_39917576的博客 162
上一篇: Block介绍(二)内存管理与其他特性
下一篇: block介绍(四)揭开神秘面纱(下)
focusjava
博客等级 码龄14年 127粉丝 528原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值