如何从Swift调用C?

时间:2014-06-02 23:10:54

标签: c swift

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

许多iOS / Apple库只有C版,我仍然希望能够调用它们。

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

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

6 个答案:

答案 0 :(得分:101)

是的,您当然可以与Apples C库进行交互。 Here解释如何。
基本上,C类型,C指针等被转换为Swift对象,例如Swift中的C intCInt

我为另一个问题构建了一个小例子,可以作为一个小解释,如何在C和Swift之间架起桥梁:

<强> main.swift

import Foundation

var output: CInt = 0
getInput(&output)

println(output)

<强> UserInput.c

#include <stdio.h>

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

<强> cliinput-桥接-Header.h

void getInput(int *output);

Here是最初的答案。

答案 1 :(得分: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)
}

请参阅文档中的Interacting with Objective-C APIs

答案 2 :(得分:5)

以防万一您和我一样对XCode感到陌生,并希望尝试Leandro's answer中发布的代码段:

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

答案 3 :(得分:3)

This post也很好地解释了如何使用clang module support进行此操作。

它在如何为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 *
}

然后在目标下 - &gt; Link Binary With Libraries我选择了添加项目并添加了libz.tbd。

您可能希望在此时构建。

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

import zlib

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

你没有 将zlib库名放在前面,除了在上面的例子中我将Swift类func命名为与C函数相同,并没有Swift资格func最终被重复调用,直到应用程序停止。

答案 4 :(得分:2)

在c ++的情况下,弹出这个错误:

  "_getInput", referenced from: 

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

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

<强> main.swift

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

<强> cliinput-桥接-Header.h

#include "UserInput.h"

这是原始video explaining this

答案 5 :(得分: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
}