如何从斯威夫特打电话给 C?

有办法从 Swift 调用 C 程序吗?

很多 iOS/Apple 库都是 C 语言的,我仍然希望能够调用它们。

例如,我希望能够从 Swift 调用 obc 运行时库。

特别是,如何连接 iOS C 头文件?

74932 次浏览

是的,你当然可以与苹果的 C 语言库进行交互。

基本上,C 类型、 C 指针等被转换成 Swift 对象,例如,Swift 中的 C intCInt

我为另一个问题做了一个小小的例子,可以作为一个小小的解释,关于如何在 C 和 Swift 之间搭建桥梁:

主要的,迅速的

import Foundation


var output: CInt = 0
getInput(&output)


println(output)

UserInput.c

#include <stdio.h>


void getInput(int *output) {
scanf("%i", output);
}

Cliinput-桥接-头文件

void getInput(int *output);

这里 是最初的答案。

编译器将 CAPI 转换为 Swift,就像它对 Objective-C 所做的那样。

import Cocoa


let frame = CGRect(x: 10, y: 10, width: 100, height: 100)


import Darwin


for _ in 1..10 {
println(rand() % 100)
}

请参阅文档中的 与 Objective-C API 交互

以防你和我一样对 XCode 不熟悉,想试试 Leandro 的回答中发布的代码片段:

  1. 档案-> 新建-> 专案
  2. 选择命令行工具作为项目预设并将项目命名为“ cliinput”
  3. 右键单击项目导航器(左侧的蓝色面板)并选择“ New File...”
  4. 在下拉对话框中,将文件命名为“ UserInput”。取消选中“同时创建一个头文件”框。单击“ Next”之后,系统会询问 XCode 是否应该为您创建 Bridge-Header.h 文件。选择“是”。
  5. 复制并粘贴莱安德鲁上述答案的代码。一旦你点击播放按钮,它应该在终端中编译和运行,在 xcode 中,它是内置在底部面板中的。如果您在终端中输入一个数字,将返回一个数字。

这篇文章 对于如何使用 clang 的 模组支援来做这件事也有很好的解释。

它是根据如何为 CommonCrypto 项目完成这项工作而设计的,但是一般来说,它应该适用于您希望在 Swift 中使用的任何其他 C 库。

我曾经为 zlib 尝试过这样做。我创建了一个新的 iOS 框架项目,并创建了一个目录 zlib,其中包含一个 module.modulemap 文件,该文件包含以下内容:

module zlib [system] [extern_c] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/zlib.h"
export *
}

然后在 Targets-> Link Binary With Library 下面,我选择 add item 并添加了 libz.tbd。

您可能需要在此时构建。

然后我就可以写下面的代码:

import zlib


public class Zlib {
public class func zlibCompileFlags() -> UInt {
return zlib.zlibCompileFlags()
}
}

你不能把 zlib 库的名字放在 前面,除非在上面的例子中,我把 Swift 类的 func 命名为和 C 函数相同的名字,如果没有这个限定,Swift func 最终会被重复调用,直到应用程序停止。

在处理指针时,它似乎是一个相当不同的球’蜡。下面是我到目前为止调用 C POSIX read系统调用的内容:

enum FileReadableStreamError : Error {
case failedOnRead
}


// Some help from: http://stackoverflow.com/questions/38983277/how-to-get-bytes-out-of-an-unsafemutablerawpointer
// and https://gist.github.com/kirsteins/6d6e96380db677169831
override func readBytes(size:UInt32) throws -> [UInt8]? {
guard let unsafeMutableRawPointer = malloc(Int(size)) else {
return nil
}


let numberBytesRead = read(fd, unsafeMutableRawPointer, Int(size))


if numberBytesRead < 0 {
free(unsafeMutableRawPointer)
throw FileReadableStreamError.failedOnRead
}


if numberBytesRead == 0 {
free(unsafeMutableRawPointer)
return nil
}


let unsafeBufferPointer = UnsafeBufferPointer(start: unsafeMutableRawPointer.assumingMemoryBound(to: UInt8.self), count: numberBytesRead)


let results = Array<UInt8>(unsafeBufferPointer)
free(unsafeMutableRawPointer)


return results
}

在 c + + 的例子中,会弹出一个错误:

  "_getInput", referenced from:

你也需要一个 c + + 头文件,将 连接添加到你的函数中,然后在桥头文件中包含头文件:

Swift 3

UserInput.h

#ifndef USERINPUT_H
#define USERINPUT_H


#ifdef __cplusplus
extern "C"{
#endif


getInput(int *output);


#ifdef __cplusplus
}
#endif

UserInput.c

#include <stdio.h>


void getInput(int *output) {
scanf("%i", output);
}

主要的,迅速的

import Foundation
var output: CInt = 0
getInput(&output)
print(output)

Cliinput-桥接-头文件

#include "UserInput.h"

这是原版的 解释这个的视频