WebAssembly 与 Swift:iOS/macOS 上的高级集成

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

面向 Swift 开发者的 WebAssembly 入门



WebAssembly (Wasm) 是一种在隔离虚拟机中运行的低级二进制指令。对于 iOS/macOS 上的 Swift 开发者来说,集成 Wasm 开辟了新的可能性:移植其他语言的代码、为第三方库提供沙箱环境,以及无需原生代码即可执行高性能计算。本文将探讨在 Swift 中使用 WebAssembly 的高级技术:从编译到直接内存操作和函数调用。



与基础教程不同,我们将聚焦于实际任务:复杂数据类型的传递、手动内存管理以及性能优化。假设您已熟悉 Swift 基础知识和 WebAssembly 的基本概念。



为 Swift 编译 WebAssembly 代码



工具与配置



要编译第三方代码(C/C++/Rust)为 Wasm,请使用 Emscripten 或 wasm-pack(适用于 Rust)。对于纯 Swift,有 SwiftWasm 项目,但其功能有限。大多数情况下,您将集成现成的模块。



将简单 C 函数编译为 Wasm 的示例:



// math.cint add(int a, int b) {    return a + b;}


命令:



emcc math.c -o math.wasm -s WASM=1 -s EXPORTED_FUNCTIONS='["_add"]' -Os


关键标志:EXPORTED_FUNCTIONS — 指定要导出的函数,-Os — 按大小优化。生成的 math.wasm 即可在 Swift 中加载。



在 Swift 中加载模块



在 iOS/macOS 上,使用 WasmKit 或 WKWebView 处理 Wasm。我们来看高级方案——使用 WasmKit 进行直接内存管理。



import WasmKit

guard let wasmData = try? Data(contentsOf: URL(fileURLWithPath: "math.wasm")) else { fatalError("无法加载 wasm")}

let module = try WasmModule(data: wasmData)let engine = WasmEngine()let instance = try engine.instantiate(module: module)

// 获取导出的函数guard let addFunc = instance.export("add")?.function else { fatalError("未找到 add 函数")}


注意:WasmKit 需要手动管理导入和内存。这提供了完全控制,但增加了复杂性。



直接内存操作:传递数组和字符串



Wasm 线性内存



WebAssembly 使用线性内存(linear memory)——一个扁平的字节数组。要传递复杂结构(数组、字符串),您必须在 Wasm 内部分配内存并复制数据。



考虑一个示例:将整数数组从 Swift 传递到计算总和的 Wasm 函数。



// sum_array.cint sum_array(int* arr, int len) {    int sum = 0;    for (int i = 0; i < len; i++) {        sum += arr[i];    }    return sum;}


编译时导出 malloc 和 free:



emcc sum_array.c -o sum_array.wasm -s WASM=1 -s EXPORTED_FUNCTIONS='["_sum_array", "_malloc", "_free"]' -s ALLOW_MEMORY_GROWTH=1


在 Swift 中分配内存、复制数据并调用函数:



let numbers = [10, 20, 30, 40, 50]let count = numbers.count

// 为数组分配内存(每个 int 4 字节)guard let mallocFunc = instance.export("malloc")?.function else { return }let ptrResult = mallocFunc.call(Int32(count * MemoryLayout<Int32>.size))guard let ptr = ptrResult?.i32 else { return }

// 将数据复制到 Wasm 内存let memory = instance.memory!for i in 0..<count { let offset = ptr + Int32(i * MemoryLayout<Int32>.size) memory.store(offset: Int(offset), value: Int32(numbers[i]))}

// 调用 sum_arrayguard let sumArrayFunc = instance.export("sum_array")?.function else { return }let result = sumArrayFun

博客

书籍推荐