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


136

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

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

たとえば、Swiftからobjcランタイムライブラリを呼び出せるようにしたいと思います。

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

回答:


106

はい、もちろんApples Cライブラリとやり取りできます。ここではその方法を説明します。
基本的に、Cタイプ、CポインターなどはSwiftオブジェクトに変換されます。たとえば、intSwiftのC はCIntです。

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

これが元の答えです。


1
私はios / swiftが初めてです。迅速なファイルで#include <asl.h>からのロギングC関数を使用したいと思います。誰でも?
Dmitry Konovalov 2014年

C ++ 、. cppファイルと互換性がありますか?
Carlos.V 2016年

C ++ファイルを直接使用するには、objective-Cラッパーを作成する必要があります。実装(それ自体は.mmファイルに常駐する必要があります)には、Objective-C ++コード(これはObjective-CとC ++を1つのファイルに収めたもの)とインターフェイス(独自の.hにある必要があります)を含める必要があります。ヘッダーファイル)には純粋なObjective-Cコードが含まれている必要があるため、実装でC ++型をObjective-C型に変換して、Swiftに公開する必要があります。その後、objective-cブリッジングヘッダーを使用してそのヘッダーをインポートできます。
ウィリアムTフログガード2016

2
「もちろん、Apples Cライブラリを操作することもできます」 不正解です。Appleだけに限らず、どんな Cライブラリで操作できます。
Slipp D. Thompson 2017

更新されたドキュメントのリンクdeveloper.apple.com/documentation/swift/...
MechEthan

9

コンパイラーは、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の操作をご覧ください。


1
例をありがとう、そうです、私はそれがどのように機能するかを見ることができます。しかし、それは実際には私の質問に答えません。それは、Apple Cライブラリを呼び出す方法でした。しかし、それは間違いなく最も近いです。
Blaze

そのドキュメントの他の場所を見てください。たとえば、CoreFoundationスタイルの「オブジェクト」(CFTypeRef)はSwiftオブジェクトに変換されます。ただし、ObjCRuntime.h関数のほとんどはSwiftにとって意味がありません。
リクスター2014年

「ただし、ObjCRuntime.h関数のほとんどはSwiftにとって意味がありません」なぜそれを言うのですか?ヘッダーをインポートしてブリッジする方法を見つける必要があると思います。それは厄介に思えますが、私はそれが進むべき道だと思います。
Blaze

5

私と同じくらいXCodeが初めてで、Leandroの回答に投稿されたスニペットを試してみたい場合に備えて:

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

4

この投稿では、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ライブラリ名を前に置く必要はありません。ただし、SwiftクラスfuncにC関数と同じ名前を付けましたが、修飾がないと、アプリケーションが停止するまでSwift funcが繰り返し呼び出されます。


3

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

  "_getInput", referenced from: 

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

スウィフト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"

これはこれを説明するオリジナルビデオです


うまくいかない場合は__OBJC、Bridging-Headerにチェックを追加してみてください。例#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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.