如何从Swift调用C?


136

有没有办法从Swift调用C例程?

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

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

特别是,如何桥接iOS C标头?

Answers:


106

是的,您当然可以与Apples C库进行交互。这里说明了如何。
基本上,C型,C指针等被翻译成对象斯威夫特,例如C int在夫特是一个CInt

我为另一个问题构建了一个小例子,该例子可以作为对C和Swift之间的桥梁:

主Swift

import Foundation

var output: CInt = 0
getInput(&output)

println(output)

UserInput.c

#include <stdio.h>

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

cliinput-Bridging-Header.h

void getInput(int *output);

是原始答案。


1
我是ios / swift的新手。我想从#include <asl.h>在swift文件中使用记录c函数。任何人?
德米特里·科诺瓦洛夫

它与C ++ 、. cpp文件兼容吗?
卡洛斯·V

要直接使用C ++文件,您应该创建一个Objective-C包装器。您将希望实现(应将其自身驻留在.mm文件中)包含Objective-C ++代码(在一个文件中仅包含Objective-C和C ++)和接口(应在其自己的.h中)头文件)应包含纯Objective-C代码,因此您必须在实现中将C ++类型转换为Objective-C类型,才能将它们公开给Swift。然后,您可以使用Objective-C桥接标头导入该标头。
William T Froggard '16

2
“您当然可以与Apples C库进行交互” 错误。您可以与任何 C库进行交互,而不仅限于Apple。
斯利普D.汤普森


9

编译器将C API转换为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交互


1
感谢您提供的示例,您是对的,我可以看到它是如何工作的。但是,它并不能真正回答我的问题,即如何调用Apple C库。但这绝对是最接近的。
Blaze 2014年

在该文档的其他地方查找。例如,将CoreFoundation样式的“对象”(CFTypeRef)转换为Swift对象。不过,大多数ObjCRuntime.h函数对Swift都没有意义。
rickster 2014年

“尽管大多数ObjCRuntime.h函数对Swift都没有意义,”为什么这么说呢?我想我需要找到某种方式导入标头并将其桥接。这似乎很尴尬,但我想这是要走的路。
Blaze 2014年

5

万一您和我一样不熟悉XCode,并想尝试Leandro的答案中发布的摘录:

  1. 文件->新建->项目
  2. 选择“命令行工具”作为项目预设,并将项目命名为“ cliinput”
  3. 右键单击项目导航器(左侧的蓝色面板),然后选择“新建文件...”。
  4. 在下拉对话框中,名称为“ UserInput”。取消选中“也创建头文件”框。单击“下一步”后,将询问XCode是否应为您创建Bridging-Header.h文件。选择“是”。
  5. 复制并粘贴上面Leandro的答案中的代码。单击播放按钮后,它应在终端中编译并运行,该终端在xcode中内置在底部面板中。如果您在终端中输入数字,则会返回一个数字。

4

这篇文章也很好地解释了如何使用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 Libraries下,我选择添加项目并添加libz.tbd。

您可能要在此时进行构建。

然后,我能够编写以下代码:

import zlib

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

你不具备把zlib库的名字在前面,除了在命名雨燕类FUNC一样的C函数上述情况下,我,没有资格雨燕FUNC最终被重复调用,直到应用程序暂停。


3

对于c ++,会弹出此错误:

  "_getInput", referenced from: 

您还需要一个c ++头文件。将c链接添加到您的函数中,然后将头文件包括在bridge-header中:

迅捷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);
}    

主Swift

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

cliinput-Bridging-Header.h

#include "UserInput.h"

这是解释这个的原始视频


如果不起作用,请尝试将__OBJC支票添加到您的桥接头中,例如#ifdef __OBJC @import UIKit; #endif
Chris Yim

1

当处理指针时,这似乎是一个完全不同的球。到目前为止,这是我要调用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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.