Jak dzwonić do C z Swift?


136

Czy istnieje sposób na wywołanie procedur C z poziomu Swift?

Wiele bibliotek iOS / Apple jest dostępnych tylko w języku C i nadal chciałbym móc je wywoływać.

Na przykład chciałbym móc wywołać biblioteki uruchomieniowe objc ze swift.

W szczególności, jak połączyć nagłówki iOS C?

Odpowiedzi:


106

Tak, możesz oczywiście współdziałać z bibliotekami Apples C. Tutaj wyjaśniono, jak to zrobić.
Zasadniczo typy C, wskaźniki C itp. Są tłumaczone na obiekty języka Swift, na przykład C intw języku Swift to CInt.

Zbudowałem mały przykład dla innego pytania, które może być użyte jako małe wyjaśnienie, jak połączyć C i 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);

Oto oryginalna odpowiedź.


1
Jestem nowy w iOS / Swift. Chciałbym użyć funkcji logowania c z #include <asl.h> w plikach Swift. Ktoś?
— Dmitry Konovalov

czy jest kompatybilny z C ++, plikami .cpp?
— Carlos V,

Aby bezpośrednio używać plików C ++, należy utworzyć opakowanie z celem-C. Będziesz chciał, aby implementacja (która powinna znajdować się sama w pliku .mm) zawierała kod Objective-C ++ (który jest po prostu Objective-C i C ++ w jednym pliku), a interfejs (który powinien znajdować się we własnym .h). nagłówek) powinien zawierać czysty kod Objective-C, więc będziesz musiał przekonwertować typy C ++ na typy Objective-C w implementacji, aby udostępnić je Swift. Następnie możesz zaimportować ten nagłówek z nagłówkiem mostkującym z celem-c.
— William T Froggard

2
„Możesz oczywiście współpracować z bibliotekami Apple C” . Niepoprawnie. Możesz wchodzić w interakcje z dowolnymi bibliotekami C, które nie są ograniczone tylko do Apple.
— Slipp D. Thompson,

Zaktualizowany link do dokumentacji developer.apple.com/documentation/swift/ ...
— MechEthan

9

Kompilator konwertuje C API na Swift, tak jak robi to dla Objective-C.

import Cocoa

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

import Darwin

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

Zobacz Interakcja z interfejsami API Objective-C w dokumentacji.


1
Dzięki za przykład i masz rację, widzę, jak to może działać. Jednak tak naprawdę nie odpowiada na moje pytanie, jak nazwać biblioteki Apple C. Ale zdecydowanie najbliżej.
— Blaze

Poszukaj w innym miejscu tego dokumentu. Na przykład „obiekty” ( CFTypeRef) w stylu CoreFoundation są konwertowane na obiekty Swift. Większość funkcji ObjCRuntime.h nie ma jednak znaczenia dla języka Swift.
— rickster

„Większość funkcji ObjCRuntime.h nie ma jednak znaczenia dla Swifta” Dlaczego tak mówisz? Myślę, że muszę znaleźć sposób na zaimportowanie nagłówka i połączenie go. Wydaje się to niezręczne, ale przypuszczam, że to właściwy sposób.
— Blaze

5

Na wypadek, gdybyś był równie nowy w XCode jak ja i chciałbyś wypróbować fragmenty zamieszczone w odpowiedzi Leandro :

  1. Plik-> Nowy-> Projekt
  2. wybierz narzędzie wiersza poleceń jako ustawienie wstępne projektu i nazwij projekt „cliinput”
  3. kliknij prawym przyciskiem myszy w nawigatorze projektu (niebieski panel po lewej) i wybierz „Nowy plik ...”
  4. W rozwijanym oknie dialogowym nazwij plik „UserInput”. Odznacz pole „Utwórz także plik nagłówkowy”. Po kliknięciu „Dalej” zostaniesz zapytany, czy XCode powinien utworzyć dla Ciebie plik Bridging-Header.h. Wybierz „Tak”.
  5. Skopiuj i wklej kod z odpowiedzi Leandro powyżej. Po kliknięciu przycisku odtwarzania powinien skompilować się i uruchomić w terminalu, który w xcode jest wbudowany w dolny panel. Jeśli wprowadzisz numer w terminalu, zostanie zwrócony numer.

4

Ten post zawiera również dobre wyjaśnienie, jak to zrobić, korzystając z obsługi modułów clang .

Jest sformułowany pod kątem tego, jak to zrobić dla projektu CommonCrypto, ale ogólnie powinien działać dla każdej innej biblioteki C, z której chcesz korzystać w Swift.

Krótko eksperymentowałem z robieniem tego dla zlib. Stworzyłem nowy projekt platformy iOS i utworzyłem katalog zlib, zawierający plik module.modulemap zawierający:

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

Następnie w obszarze Cele -> Połącz pliki binarne z bibliotekami wybrałem dodaj elementy i dodałem libz.tbd.

W tym momencie możesz chcieć zbudować.

Byłem wtedy w stanie napisać następujący kod:

import zlib

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

Nie musisz umieszczać nazwy biblioteki zlib na początku, z wyjątkiem powyższego przypadku, gdy nazwałem funkcję klasy Swift tak samo jak funkcję C, a bez kwalifikacji funkcja Swift jest wywoływana wielokrotnie, aż aplikacja się zatrzyma.


3

W przypadku C ++ pojawia się następujący błąd:

  "_getInput", referenced from: 

Potrzebujesz też pliku nagłówkowego C ++. Dodaj link-c do swojej funkcji, a następnie dołącz plik nagłówkowy do nagłówka mostu:

Szybki 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"

Oto oryginalny film wyjaśniający to


jeśli to nie działa, spróbuj dodać __OBJCczek do swojego nagłówka Bridging-Header, np.#ifdef __OBJC @import UIKit; #endif
— Chris Yim

1

Wydaje się, że to trochę inna kula wosku, gdy mamy do czynienia ze wskazówkami. Oto, co mam do tej pory do wywoływania wywołania readsystemowego C POSIX :

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
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.