工程swift与OC混编改造

news2024/11/29 10:32:19

最近公司项目准备引入swift,由于目前工程已经完成了组件化不再是简单的单仓工程,所以需要进行混编改造。下面记录一下自己对工程进行混编改造的思考以及过程。

混编原理

看了很多文档,比较少有讲混编原理的,这里简单介绍一下语言进行混编的基本逻辑,因为我们都知道swift和OC可以混编,但是他们为啥能混编呢?

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.002.png

首先简单的过一下编译以及静态链接的流程:

  1. 预编译阶段,B.m将引入A.h以及B.h全部copy到B.m(这种预编译方式我们称它为文本模型)

  2. 编译阶段,B.m将B自身的方法地址全部编入符号表,但是编译的过程会碰到对A.function的调用,这个时候编译器检查A.function是否有声明,明确有(跟着A.h头文件copy过来的),但是并没有相应的实现,因此会将A.function的引用做一个特殊标记,并在重定位表中记录,等待静态链接成功后修改为指向A.function真实所在的地址

  3. 完成所有.o文件的符号表的合并,根据重定位表中的记录需要重新修改符号的地址,完成真实的A.function地址的特殊标记替换。

整个过程中只要生成的A.o以及B.o中间文件是按照编译器预期的格式组织的,那么静态链接器ld的就能根据格式缝合成最终产物:可运行的app。因此无论A和B是用什么语言编写的,只要编译器B本身能够识别出对应的语言A的声明,那么就可以在B的编译过程中标记A.function,并产出规定格式的.o文件。最终在静态链接器的帮助下完成对A.function的调用。(由上也可以看出头文件其实仅仅承担了符号标记的作用,其余的编译部分都是在实现文件中完成的)
Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.003.png

以上就是语言混编的基础。

备注:JAVA的编译逻辑稍有不同,这里只讨论java转class,不讨论class通过虚拟机变成真实地址的过程,java不像c系语言一样由链接器寻找最终符号地址,他们是由写代码的人直接使用全限定词指定符号在哪个文件,因此java编译成class以后符号地址就确定了,这也是java不需要头文件的逻辑,java与kotlin混编只需要保证编译出来的字节码标准统一即可混编,因为编译某个文件对其他文件有引用,编译器会立刻对引用文件进行编译生成class,由于java和kotlin都可以识别出字节码的规则,因此可以识别出相应的方法调用从而完成混编

swift与OC混编

了解了语言混编的基础之后我们再来落实到具体的语言:swift与OC的混编。

1. swift调用OC

swift的编译器swiftc包含了clang编译器的大量的功能,因此swift调用OC,其实就是依赖于swiftc将OC文件的声明转成swift的声明(.swiftiinterface),然后swift直接使用swift语法的OC方法声明,完成了对OC方法实现的调用。如下图所示(都是xcode自动生成的)

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.004.jpeg

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.005.jpeg

2. OC调用swift

因为历史原因,OC的编译器clang是无法识别swift语言的,因此要想让OC可以识别swift的声明需要依赖于swiftc编译器将swift声明转成OC声明(FZCache-swift.h),然后OC直接使用OC语法的swift方法声明,完成对swift方法实现的调用。

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.006.jpeg

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.007.jpeg

不管是swift声明转OC声明,还是OC声明转swift声明,以上两个过程中都暗含了一个关键的因素:头文件的查找,只有找到相应的声明转换后的头文件的位置,才能访问到方法声明。才能最终完成混编。

头文件查找

1. 基于文本模型的头文件查找

就是我们常规使用的方案#import头文件,跟#include本身区别不大,除了会自动去重,但是他们处理头文件的逻辑是一样的,每次编译一个.m文件都要重新对此.m文件中的头文件引入进行复制粘贴,因此理论上时间复杂度为O(m*n),另外由于采用的是复制粘贴替换的逻辑,因此在处理一些宏定义的时候容易出错,比如可能会存在某个定义

#define nonatomic @"nonatomic"

这个宏定义可能在某个文件中是没有任何问题的,但是如果有人使用了@property (nonatomic)这样的属性的时候就会导致代码出现错误,关键由于预编译采用的是复制的方式,即便是编译器再次报错,也会让间接引入了这个宏定义声明的开发者一下子难以查找到真正的问题位置

2. clang module

基于以上的问题,苹果提出了clang module的头文件查找方案,该方案声明了一种特定的文件组织形式,以静态库为例,静态库分成两种.a和framework,clang module规定静态库必须以如下方式进行资源的组织

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.008.png

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.009.png

module使用以下方案对头文件进行访问:

@import FZCache.FZKVCache

当编译器读取到FZCache的时候就会从特定存储路径中查看是否存在有FZCache这个组件空间(也就是这里说的module),然后查询其中是否有FZKVCache的缓存产物,如果有则直接引用,如果没有就先找到FZCache.framework这个文件夹,然后进入Headers文件夹查询FZKVCache.h头文件,如果可以找到,再进入Modules文件查看是否有modulemap文件,如果有则启用module,在特定存储路径中创建一份单独的编译空间用于存放预编译缓存,否则报错,确认有modulemap文件后继续查看此描述文件中是否包含了FZKVCache.h,

framework module FZCache {
  umbrella header "FZCache-umbrella.h"

  export *
  module * { export * }
}

module FZCache.Swift {
  header "FZCache-Swift.h"
  requires objc
}
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

#import "FZCache.h"
#import "FZKVCache.h"

FOUNDATION_EXPORT double FZCacheVersionNumber;
FOUNDATION_EXPORT const unsigned char FZCacheVersionString[];

显然FZCache-umbrella.h头文件中是包含FZKVCache.h的,因此编译生成FZKVCache的预编译产物放入FZCache module空间中,以备下次使用。

所以启用了clang module以后,组件只需编译一次,从理论上极大的降低了编译时间。

swiftmodule可以认为是clang module的升级版,基本上逻辑大同小异,但是针对swift的有一些特定的优化,我们可以简单的把swiftmodule和modulemap对应起来。在swiftmodule文件中存储了对整个模块以及模块内部子模块的二进制描述。
Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.010.png

由于swiftc编译器只能通过clang module和swiftmodule识别到framework的头文件(如果是本target内部的头文件其实swiftc是能直接识别出来的,比如在主仓中swift通过桥接文件写入#import oc的头文件就可以识别到这些头文件),因此如果需要在swift仓库中引入OC仓库,就必须要对OC仓库进行clang module化。

备注:xcode对#import <A/A.h>做了优化,如果确认能找到modulemap文件,则启用clang module编译,转成@import A.A,如果未能找到则转成我们普通的#include <A/A.h>文本复制替换操作

鉴于目前所有的仓库都有可能需要使用混编,因此需要对所有的组件进行clang module化。

实现方案

我们将基于cocoapods完成所有仓库的module化。开启方法有多种。

1.use_framework!

2.use_modular_headers

3.自己写脚本生成modulemap,并组织好头文件。

这里我们选用use_framework!选项,即在开启所有仓库module化的同时,将生成产物从.a转变为framework.碰到头文件报错的位置就修改引用方式解决问题。要注意的是module化具有传递性,如果A开启了module,但是A依赖的B没有开启module,编译器就会报错。

使用方式

子仓的互相调用模式:

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.011.png

主工程内部调用方式

Aspose.Words.bb029009-75e5-4517-a727-0bf2a92b9c56.012.png

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

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

相关文章

第十九章_手写Redis分布式锁

锁的种类 单机版同一个JVM虚拟机内synchronized或者Lock接口。 分布式多个不同JVM虚拟机&#xff0c;单机的线程锁机制不再起作用&#xff0c;资源类在不同的服务器之间共享了。 一个靠谱分布式锁需要具备的条件和刚需 独占性 &#xff1a;OnlyOne&#xff0c;任何时刻只能有且…

linux-静态库制作与使用

创建2个目录进行创建与使用的演示 创建静态库 准备源文件与头文件 查看所有源文件与头文件 将源文件编译.o文件&#xff0c;然后将.o文件打包为静态库 gcc -c mymath.c -o mymath.o -stdc99 gcc -c myprint.c -o myprint.o -stdc99 ar指令&#xff1a;打包多个.o文件为静态…

Fast-RCNN理论基础

一&#xff1a;总体流程 1、将图像输入网络得到相应的特征图。 2、使用RPN结构生成候选框&#xff0c;将RPN生成的候选框投影到特征图上获得相应的特征矩阵。 3、将每个特征矩阵通过ROI pooling层缩放到7x7大小的特征图&#xff0c;接着将特征图展平通过一系列全连接层得到预…

测试老鸟整理,从手工进阶自动化测试,自动化之路清晰通透...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

主啊,我甚至不知道从何说起...

主啊,我甚至不知道从何说起...欢迎来到费米悖论。 外面有太多令人恐惧的事物。 我不会一一说过。然而,我读到的一件事情让我感到恐惧,我希望它也让你感到恐惧。 那么,就是这样... 这一切与一个问题有关:如果他们确实存在,为什么还没有来访?可怕的就是这些可能性。 根据《弄清外…

PCB布局思路分析 让你的布局从此简单!

分析好整个电路原理以后&#xff0c;就可以开始对整个电路进行布局布线&#xff0c;这一期&#xff0c;给大家介绍一下布局的思路和原则。1、首先&#xff0c;我们会对结构有要求的器件进行摆放&#xff0c;摆放的时候根据导入的结构&#xff0c;连接器得注意1脚的摆放位置。 ​…

财务共享中心搭建以后,如何进行精细化管理?

财务共享中心通过统一的财务流程、系统和人员配置实现了财务业务的标准化和规范化&#xff0c;为企业的财务管理提供了很大的便捷性和效率性&#xff0c;目前许多大型企业已纷纷开始搭建自己的财务共享中心。 但企业在搭建财务共享中心之后&#xff0c;往往会在运行初期遇上业…

Qt 帮助框架使用

前面我们已经简单了解了Qt帮助框架&#xff0c;本节我们将举例说明生成Qt帮助集&#xff0c;并自定义Qt Assistant。 准备工作 因为创建帮助系统建立帮助文件的前提是HTML文档文件已经存在&#xff0c;所以我们来弄一些简单的HTML文档&#xff08;难的我还不会&#xff09;。…

使用exe4j和Inno Setup把jar包转成exe

使用exe4j和Inno Setup把jar包转成exe exe4j下载地址&#xff1a;https://www.ej-technologies.com/download/exe4j/version_60 Inno Setup地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Lh0JUuQgB6bkbACIx6MqdQ 提取码&#xff1a;dfox 一、exe4j将jar装车exe…

【OpenMMLab AI实战营第二期】二十分钟入门OpenMMLab笔记

OpenMMlab 主页&#xff1a;openmmlab.com 开源地址&#xff1a;https://github.com/open-mmlab 学习视频地址&#xff1a;https://www.bilibili.com/video/BV1js4y1i72P/ 概述 开源成为人工智能行业发展引擎 时间轴 theano&#xff1a;2007 Caffe&#xff1a;2013 Ten…

如何学习 WPF 详细教程

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

SPI通信以及与W2Q564(ROM)交换(读写)数据

一.SPI协议简介 SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface)&#xff0c;即串行外围设备接口&#xff0c;允许芯片与外部设备以全双工、同步、串行方式通信。此接口可以被配置成主模式&#xff0c;并为外部从设备提供通信时钟(SCK)。接口还能以多主配…

uniapp(一) 之 小程序与uniapp 基础

uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到 i OS 、 Android 、 Web &#xff08; 响应式&#xff09;、 以及各种小程序&#xff08;微信 / 支付宝 / 百度 / 头条 / 飞书 /QQ/ 快手 / 钉 钉 / 淘宝&#xff09;、 …

工程测量仪器:工程安全的保障者

工程仪器是现代工程建设中必不可少的工具&#xff0c;它们可以帮助企业对工程进行监控和管理&#xff0c;从而提高工程运行效率和安全性。在当前的工程建设领域&#xff0c;安全运营已成为企业的首要任务&#xff0c;而工程仪器正是实现这个目标的重要保障之一。 渗压计广泛应用…

数据(浮点数)在内存中的存储(2)

目录 浮点数家族 浮点数类型在内存中的存储 一.为什么说整型和浮点数在内存中存储方式不同&#xff08;证明&#xff09; 二.浮点数的存储规则 浮点数在计算机内部的表示方法 1.对于M的存储和取出规则 2.对于E的存储和取出时的规则 对前面代码结果进行解释&#xff1a; …

tinkerCAD基础操作

放大尺寸&#xff01; 让我们通过调整大小来更改基本框形状&#xff01; 说明 继续执行下一步。 扩展每个块 每个“框”形状的大小都与提示匹配。 说明 通过左键单击形状来选择一个框。 这将启用形状控点。 使用每个形状底部边缘的黑色手柄在单个方向上调整形状的大小。 使…

nginx+keepalive高可用搭建方案

一、什么是nginx有什么作用 nginx是一款使用非常广泛的Web服务器&#xff0c;它可以提供高性能和可扩展性。它是由Google开发的&#xff0c;并且是Apache HTTP Server的替代品。 以下是一些nginx的主要特点&#xff1a; 轻量级&#xff1a;nginx比Apache轻量级&#xff0c;它…

ChatGPT与软件架构(1) - 快速原型

通过ChatGPT生成设计和原型代码&#xff0c;可以帮助团队快速启动项目&#xff0c;验证想法&#xff0c;提高效率。原文: ChatGPT and Software Architecture Surfing Croyde Bay Unsplash OpenAI的ChatGPT现在越来越火&#xff0c;出现了各种有趣用例。 从许多方面来看&#x…

为何溃坝事故频发,大坝安全如何保障?

随着水利水电工程的重要性日益突显&#xff0c;水库大坝安全越来越受到相关部门的重视。因为大坝的安全直接影响水利工程的功能与作用&#xff0c;因此对大坝安全的监测显得十分必要。大坝安全监测的作用是能够及时掌握大坝的运行状态&#xff0c;及时发现大坝的变形、渗漏等异…

DB-GPT数据库GPT,支持本地部署,可以在私有环境中运行!!

DB-GPT 是什么&#xff1f; 随着大模型的发布迭代&#xff0c;大模型变得越来越智能&#xff0c;在使用大模型的过程当中&#xff0c;遇到极大的数据安全与隐私挑战。在利用大模型能力的过程中我们的私密数据跟环境需要掌握自己的手里&#xff0c;完全可控&#xff0c;避免任何…