web-dev-qa-db-ja.com

SwiftからCを呼び出す方法は?

SwiftからCルーチンを呼び出す方法はありますか?

多くのiOS/AppleライブラリはCのみであり、それらを呼び出すことができるようにしたいです。

たとえば、Swiftからobjcランタイムライブラリを呼び出すことができます。

特に、iOS Cヘッダーをどのようにブリッジしますか?

130
Blaze

はい、もちろんApples Cライブラリとやり取りできます。 ここ の方法を説明します。
基本的に、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-Bridging-Header.h

void getInput(int *output);

ここ は元の答えです。

101
Leandros

コンパイラは、Objective-Cの場合と同様に、C APIをSwiftに変換します。

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との対話 を参照してください。

9
rickster

あなたが私と同じようにXCodeを初めて使い、 Leandroの答え に投稿されたスニペットを試してみたい場合に備えて:

  1. ファイル->新規->プロジェクト
  2. プロジェクトのプリセットとしてコマンドラインツールを選択し、プロジェクトに「cliinput」という名前を付けます
  3. プロジェクトナビゲータ(左側の青いパネル)を右クリックして、「新規ファイル...」を選択します
  4. ドロップダウンダイアログで、ファイルに「UserInput」という名前を付けます。 [ヘッダーファイルも作成する]チェックボックスをオフにします。 [次へ]をクリックすると、XCodeがBridging-Header.hファイルを作成するかどうかを尋ねられます。 「はい」を選択します。
  5. 上記のLeandroの回答からコードをコピーして貼り付けます。再生ボタンをクリックすると、ターミナルでコンパイルおよび実行されるはずです。ターミナルは、xcodeの下部パネルに組み込まれています。端末に数字を入力すると、数字が返されます。
5
lukas83

この投稿 には、clangの モジュールサポート を使用してこれを行う方法に関する適切な説明もあります。

CommonCryptoプロジェクトでこれを行う方法の観点からフレーム化されていますが、一般的には、Swift内から使用する他のCライブラリでも機能するはずです。

Zlibでこれを簡単に試しました。新しいiOSフレームワークプロジェクトを作成し、次の内容のmodule.modulemapファイルを含むディレクトリzlibを作成しました。

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

次に、[ターゲット]-> [ライブラリとバイナリをリンク]で、[アイテムの追加]を選択し、libz.tbdを追加しました。

この時点でビルドすることもできます。

その後、次のコードを書くことができました。

import zlib

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

上記の場合を除いて、zlibライブラリ名を前に置くhaveをしないでくださいSwift class func the C関数と同じで、修飾がないと、Swift funcは、アプリケーションが停止するまで繰り返し呼び出されます。

3
Julian

C++の場合、ポップアップするこのエラーがあります:

  "_getInput", referenced from: 

C++ヘッダーファイルも必要です。関数に c-linkage を追加し、ブリッジヘッダーにヘッダーファイルを含めます。

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-Bridging-Header.h

#include "UserInput.h"

元の これを説明するビデオ

2
John James

ポインターを扱う場合、それはかなり異なるボールのように見えます。 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
}
1
Chris Prince