Wie rufe ich C von Swift aus an?

136

Gibt es eine Möglichkeit, C-Routinen von Swift aus aufzurufen?

Viele iOS / Apple-Bibliotheken sind nur C-Bibliotheken und ich möchte diese trotzdem aufrufen können.

Zum Beispiel möchte ich in der Lage sein, die objc-Laufzeitbibliotheken von swift aufzurufen.

Wie überbrücken Sie insbesondere iOS C-Header?

Blaze
quelle

Antworten:

106

Ja, Sie können natürlich mit Apples C-Bibliotheken interagieren. Hier wird erklärt wie.
Grundsätzlich werden die C-Typen, C-Zeiger usw. in Swift-Objekte übersetzt, z. B. ist ein C intin Swift ein CInt.

Ich habe ein winziges Beispiel für eine andere Frage erstellt, die als kleine Erklärung verwendet werden kann, wie man eine Brücke zwischen C und Swift schlägt:

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

Hier ist die ursprüngliche Antwort.

Leandros
quelle
1
Ich bin neu bei ios / swift. Ich möchte die Protokollierungs-C-Funktion von #include <asl.h> in schnellen Dateien verwenden. Jemand?
Dmitry Konovalov
Ist es kompatibel mit C ++, CPP-Dateien?
Carlos.V
Um C ++ - Dateien direkt zu verwenden, sollten Sie einen Objective-C-Wrapper erstellen. Sie möchten, dass die Implementierung (die sich in einer .mm-Datei befinden sollte) Objective-C ++ - Code (der nur Objective-C und C ++ in einer Datei enthält) und die Schnittstelle (die in einer eigenen .h-Datei enthalten sein sollte) enthält Header-Datei) sollte reinen Objective-C-Code enthalten, daher müssen Sie C ++ - Typen in der Implementierung in Objective-C-Typen konvertieren, um sie für Swift verfügbar zu machen. Anschließend können Sie diesen Header mit einem Objective-C-Bridging-Header importieren.
William T Froggard
2
"Sie können natürlich mit Apples C-Bibliotheken interagieren" Falsch. Sie können mit allen C-Bibliotheken interagieren , ohne auf Apple beschränkt zu sein.
Slipp D. Thompson
Aktualisierter Dokumentationslink developer.apple.com/documentation/swift/…
MechEthan
9

Der Compiler konvertiert die C-API genau wie für Objective-C in Swift.

import Cocoa

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

import Darwin

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

Siehe Interaktion mit Objective-C-APIs in den Dokumenten.

Rickster
quelle
1
Vielen Dank für das Beispiel, und Sie haben Recht, ich kann sehen, wie das funktionieren könnte. Es beantwortet jedoch nicht wirklich meine Frage, wie man Apple C-Bibliotheken aufruft. Aber es ist definitiv am nächsten.
Blaze
Suchen Sie an anderer Stelle in diesem Dokument. Beispielsweise werden "Objekte" ( CFTypeRef) im CoreFoundation-Stil in Swift-Objekte konvertiert. Die meisten Funktionen von ObjCRuntime.h sind für Swift jedoch nicht von Bedeutung.
Rickster
"Die meisten Funktionen von ObjCRuntime.h sind für Swift jedoch nicht von Bedeutung." Warum sagen Sie das? Ich denke, ich muss einen Weg finden, einen Header zu importieren und ihn zu überbrücken. Das scheint unangenehm, aber ich nehme an, es ist der richtige Weg.
Blaze
5

Nur für den Fall, dass Sie so neu bei XCode sind wie ich und die in Leandros Antwort veröffentlichten Schnipsel ausprobieren möchten :

  1. Datei-> Neu-> Projekt
  2. Wählen Sie das Befehlszeilen-Tool als Projektvoreinstellung und nennen Sie das Projekt "cliinput".
  3. Klicken Sie mit der rechten Maustaste in den Projektnavigator (das blaue Feld links) und wählen Sie "Neue Datei ...".
  4. Benennen Sie im Dropdown-Dialogfeld die Datei "UserInput". Deaktivieren Sie das Kontrollkästchen "Erstellen Sie auch eine Header-Datei". Sobald Sie auf "Weiter" klicken, werden Sie gefragt, ob XCode die Datei Bridging-Header.h für Sie erstellen soll. Wählen Sie "Ja".
  5. Kopieren Sie den Code aus der obigen Antwort von Leandro und fügen Sie ihn ein. Sobald Sie auf die Wiedergabetaste klicken, sollte diese kompiliert und im Terminal ausgeführt werden, das in xcode im unteren Bereich integriert ist. Wenn Sie im Terminal eine Nummer eingeben, wird eine Nummer zurückgegeben.
lukas83
quelle
4

Dieser Beitrag enthält auch eine gute Erklärung dazu, wie dies mithilfe der Modulunterstützung von clang durchgeführt werden kann .

Es wird festgelegt, wie dies für das CommonCrypto-Projekt ausgeführt wird. Im Allgemeinen sollte es jedoch für jede andere C-Bibliothek funktionieren, die Sie in Swift verwenden möchten.

Ich habe kurz damit experimentiert, dies für zlib zu tun. Ich habe ein neues iOS-Framework-Projekt erstellt und ein Verzeichnis zlib erstellt, das eine module.modulemap-Datei mit den folgenden Angaben enthält:

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

Dann habe ich unter Ziele -> Binär mit Bibliotheken verknüpfen die Option Elemente hinzufügen und libz.tbd hinzugefügt.

Möglicherweise möchten Sie an dieser Stelle erstellen.

Ich konnte dann folgenden Code schreiben:

import zlib

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

Sie müssen nicht haben , um vor der zlib - Bibliothek Namen zu setzen, außer im obigen Fall ich die Swift - Klasse func die gleiche wie die C - Funktion genannt, und ohne die Qualifikation der Swift func endet oft , bis die Anwendung stoppt aufgerufen wird.

julianisch
quelle
3

Im Fall von c ++ tritt folgender Fehler auf:

  "_getInput", referenced from: 

Sie benötigen auch eine C ++ - Headerdatei. Fügen Sie Ihrer Funktion eine C-Verknüpfung hinzu und fügen Sie dann die Header-Datei in den Bridge-Header ein:

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"

Hier ist das Originalvideo , das dies erklärt

John James
quelle
Wenn es nicht funktioniert, versuchen Sie __OBJC, Ihrem Bridging-Header einen Scheck hinzuzufügen , z. B.#ifdef __OBJC @import UIKit; #endif
Chris Yim,
1

Beim Umgang mit Zeigern scheint es sich um eine etwas andere Wachskugel zu handeln. Folgendes habe ich bisher für den Aufruf des C POSIX read-Systemaufrufs:

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
}
Chris Prince
quelle