Objective C block背后的黑魔法

2023-10-17 19:30

本文主要是介绍Objective C block背后的黑魔法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

block在Objective C开发中应用非常广泛,我们知道block会捕获外部对象,也知道使用block要防止循环引用。

“知其然而不知其所以然”是一件很痛苦的事情,那么block这套机制在OC中是如何实现的呢?本文通过从C/C++到汇编层面分析block的实现原理。


Clang

clang是XCode的编译器前端,编译器前端负责语法分析,语义分析,生成中间代码(intermediate representation )。

比如当你在XCode中进行build一个.m文件的时候,实际的编译命令如下

clang -x objective-c -arch x86_64-fmessage-length=0 -fobjc-arc... -Wno-missing-field-initializers ... -DDEBUG=1 ... -isysroot iPhoneSimulator10.1.sdk -fasm-blocks ... -I headers.hmap -F 所需要的Framework  -iquote 所需要的Framework  ... -c ViewController.m -o ViewController.o

Objective C也可以用GCC来编译,不过那超出了本文的范畴,不做讲解。

Clang除了能够进行编译之外,还有其他一些用法。比如本文分析代码的核心命令就是这个:

clang -rewrite-objc 文件.m

通过这个命令,我们可以把Objective C的代码用C++来表示。

对于想深入理解Clang命令的同学,可以用命令忙自带的工具来查看帮助文档

man clang

或者阅读官方文档:文档地址。


查看汇编代码

在XCode中,对于一个源文件,我们可以通过如下方式查看其汇编代码。这对我们分析代码深层次的实现原理非常有用,这个在后面也会遇到。


Objective C对象内存模型

为了本文讲解的更清楚,我们首先来看看一个Objective C对象的内存模型。我们首先新建一个类,内容如下

DemoClass.h

@interface DemoClass : NSObject
@property (nonatomic, copy) NSString * value;
@end

DemoClass.m

@implementation DemoClass
- (void)demoFunction{DemoClass * obj = [[DemoClass alloc] init];
}
@end

然后,我们用上文提到的Clang命令将DemoClass.m转成C++的表示。

clang -rewrite-objc DemoClass.m

转换完毕后当前目录会多一个DemoClass.cpp文件,这个文件很大,接近十万行。

我们先搜索这个方法名称demoFunction,以方法作为切入

static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {DemoClass * obj = ((DemoClass *(*)(id, SEL))(void *)objc_msgSend)((id)((DemoClass *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("DemoClass"), sel_registerName("alloc")), sel_registerName("init"));
}

可以看到,转换成C++后,一个实例方法转换为一个静态方法,这个方法的内容看起来很乱,因为有各种的类型强制转换,去掉后就比较清楚了。

static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {DemoClass * obj = objc_msgSend(objc_msgSend(objc_getClass("DemoClass"), sel_registerName("alloc")), sel_registerName("init"));
}

可以看到:

  • 转换后增加了两个参数:self_cmd
  • 方法的调用转换成了objc_msgSend,这是一个C函数,两个参数分别是ClassSEL

关于objc_msgSend内发生的事情,参见我之前的一篇博客:

  • iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)

到这里,我们知道了一个OC的实例方法具体是怎么实现的了。

那么,一个OC对象在内存中是如何存储的呢?我们在刚刚的方法的上下可以找到这个类的完整实现,

//类对应的结构体
struct DemoClass_IMPL {struct NSObject_IMPL NSObject_IVARS;NSString *_value;
};
//demoFunction方法
static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {DemoClass * obj = objc_msgSend(objc_msgSend(objc_getClass("DemoClass"), sel_registerName("alloc")), sel_registerName("init"));
}
//属性value的getter方法
static NSString * _I_DemoClass_value(DemoClass * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_DemoClass$_value)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);//属性value的setter方法
static void _I_DemoClass_setValue_(DemoClass * self, SEL _cmd, NSString *value) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct DemoClass, _value), (id)value, 0, 

这篇关于Objective C block背后的黑魔法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/227479

相关文章

[Linux Kernel Block Layer第一篇] block layer架构设计

目录 1. single queue架构 2. multi-queue架构(blk-mq)  3. 问题 随着SSD快速存储设备的发展,内核社区越发发现,存储的性能瓶颈从硬件存储设备转移到了内核block layer,主要因为当时的内核block layer是single hw queue的架构,导致cpu锁竞争问题严重,本文先提纲挈领的介绍内核block layer的架构演进,然

重启顺风车的背后,是高德难掩的“野心”

以史鉴今,我们往往可以从今天的事情中,看到古人的智慧,也看到时代的进步。就如西汉后期文学家恒宽曾说的,“明者因时而变,知者随事而制”。 图源来自高德官方 近日,高德就展现了这样的智慧。在网约车市场陷入饱和状态时,高德审时度势,宣布重启顺风车业务,并在全国范围内大规模启动,首批覆盖珠三角、长三角及湖北省武汉市等共计65座城市,完成在出行服务领域的又一重要布局。 重启顺风车,增量市场的“蛋糕

黑神话悟空背后的技术揭秘与代码探秘

《重塑神话:黑神话悟空背后的技术揭秘与代码探秘》 引言 在国产游戏领域,《黑神话:悟空》无疑是一颗璀璨的明星,它不仅融合了深厚的中国文化元素,更在技术上实现了诸多突破,为玩家带来了前所未有的沉浸式体验。本文将深入剖析《黑神话:悟空》背后的关键技术,并通过代码案例展示其技术实现的魅力。 一、高精度动作捕捉技术 《黑神话:悟空》中的角色动作之所以如此逼真,得益于高精度动作捕捉技术的应用

block对变量捕获的方式

之前见很多文章对block捕获变量的方法,会进行诸如此类的描述:“block会捕获被引用的变量, 并对其进行copy操作, 因此, 可能会导致其引用计数加1,如果处理不好, 可能因循环引用导致内存泄漏。” 实际上, 这种说法并不严谨。block对变量的捕获, 根据变量类型的不同,会采用不同的捕获方式。 (1)静态或者全局变量, 在block中直接是指针传递的方式传入block中,对其进行的操作

Linux block_device gendisk和hd_struct到底是个啥关系

本文的源码版本是Linux 5.15版本,有图有真相: 1.先从块设备驱动说起 安卓平台有一个非常典型和重要的块设备驱动:zram,我们来看一下zram这个块设备驱动加载初始化和swapon的逻辑,完整梳理完这个逻辑将对Linux块设备驱动模型有深入的理解。 zram驱动加载的时候会调用zram_add函数,源码如下: 1887/*1888 * Allocate and initia

【C++】C++ STL探索:Vector使用与背后底层逻辑

C++语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C++内存管理模板初阶String使用String模拟实现 在string类文章中提及了STL容器间的接口是大差不差的,本篇将直接通过模拟实现Vector来讲解底层实现与使用。 🌈个人主页:是店小二呀 🌈C语言笔记专栏:C语言笔

Docker核心原理解读:深度剖析Docker Daemon,掌控容器背后的引擎

容器技术已经成为现代应用程序开发和部署中的核心工具,而在Docker生态系统中,Docker Daemon 扮演着至关重要的角色。它不仅是Docker架构的核心,还负责容器的管理、镜像的操作、资源的分配等复杂任务。本文将深入解读Docker Daemon的工作原理,探讨它在Docker系统中如何高效运行,以及它如何与其他组件协同工作。 一、Docker架构回顾 在深入了解Docker Daem

<编码:隐匿在计算机软硬件背后的语言>示例电路列表

<<编码: 隐匿在计算机软硬件背后的语言>>一书中的示例电路的线上可交互示例. 关于 <<编码: 隐匿在计算机软硬件背后的语言>> 一书的介绍, 参考豆瓣的介绍: 分章节介绍包含了各章节示例电路的简要操作说明, 电路截图以及在线交互操作链接, 方便读者按图查找. 点击以下或左侧书签栏各章节链接进入章节介绍. 第4章 手电筒剖析(Anatomy of a Flashlight) 示例电路

Oracle - ORA-01789: Query block has incorrect number of result columns

一、原因     这个错误一般是在执行表之间的相加(union),相减(minus)等SQL语句时,两个个查询块具有不一致的结果列数所导致的。 二、方案     只要将两段SQL语句的列数调整为一致就可以解决。使用union时,要注意数据库字段的格式要一致,如varchar和nvarchar是不一样的。

Core Text Objective-C Wrapper

Core Text Objective-C Wrapper https://github.com/akosma/CoreTextWrapper Introduction(介绍) One of the most promising and mysterious new frameworks introduced in iOS 3.2 is Core Text. Apple define