Come chiamare C da Swift?


136

C'è un modo per chiamare le routine C da Swift?

Molte librerie iOS / Apple sono solo in C e mi piacerebbe comunque poterle chiamare.

Ad esempio, mi piacerebbe poter chiamare rapidamente le librerie di runtime objc.

In particolare, come si collegano le intestazioni di iOS C?

Risposte:


106

Sì, puoi ovviamente interagire con le librerie Apples C. Ecco spiegato come.
Fondamentalmente, i tipi C, i puntatori C, ecc. Vengono tradotti in oggetti Swift, ad esempio una C intin Swift è aCInt .

Ho creato un piccolo esempio, per un'altra domanda, che può essere usata come una piccola spiegazione, su come creare un ponte tra C e 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);

Ecco la risposta originale.


1
Sono nuovo di iOS / Swift. Vorrei usare la funzione c di registrazione da #include <asl.h> nei file rapidi. Chiunque?
Dmitry Konovalov,

è compatibile con i file C ++, .cpp ??
Carlos.V,

Per utilizzare direttamente i file C ++, è necessario creare un wrapper obiettivo-C. Vorrai che l'implementazione (che dovrebbe risiedere da sola in un file .mm) contenga il codice Objective-C ++ (che è solo Objective-C e C ++ in un file) e l'interfaccia (che dovrebbe essere nel suo .h file di intestazione) dovrebbe contenere puro codice Objective-C, quindi sarà necessario convertire i tipi C ++ in tipi Objective-C nell'implementazione, per esporli a Swift. Quindi è possibile importare quell'intestazione con un'intestazione bridge obiett-c.
William T Froggard,

2
"Puoi ovviamente interagire con le librerie di Apple C" Errato. Puoi interagire con qualsiasi libreria C, per niente limitata solo a quella di Apple.
Slipp D. Thompson,

Link alla documentazione aggiornato developer.apple.com/documentation/swift/…
MechEthan

9

Il compilatore converte l'API C in Swift proprio come fa per Objective-C.

import Cocoa

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

import Darwin

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

Vedi Interazione con le API di Objective-C nei documenti.


1
Grazie per l'esempio e hai ragione, posso vedere come potrebbe funzionare. Tuttavia, in realtà non risponde alla mia domanda, che era come chiamare le librerie di apple C. Ma è sicuramente il più vicino.
Blaze,

Cerca altrove in quel documento. Ad esempio, gli "oggetti" in stile CoreFoundation ( CFTypeRef) vengono convertiti in oggetti Swift. Tuttavia, la maggior parte delle funzioni di ObjCRuntime.h non sono significative per Swift.
rickster,

"La maggior parte delle funzioni di ObjCRuntime.h non sono significative per Swift, sebbene" Perché lo dici? Penso di dover trovare un modo per importare un'intestazione e collegarla. Sembra imbarazzante, ma suppongo che sia la strada da percorrere.
Blaze,

5

Nel caso in cui tu sia nuovo su XCode come me e desideri provare i frammenti pubblicati nella risposta di Leandro :

  1. File-> Nuovo-> Progetto
  2. scegli Command Line Tool come preset di progetto e dai un nome al progetto "cliinput"
  3. fai clic con il tasto destro nel navigatore del progetto (il pannello blu a sinistra) e scegli "Nuovo file ..."
  4. Nella finestra di dialogo a discesa, denominare il file "UserInput". Deseleziona la casella "Crea anche un file di intestazione". Dopo aver fatto clic su "Avanti" ti verrà chiesto se XCode dovrebbe creare il file Bridging-Header.h per te. Scegli "Sì".
  5. Copia e incolla il codice dalla risposta di Leandro sopra. Dopo aver fatto clic sul pulsante di riproduzione, dovrebbe essere compilato ed eseguito nel terminale, che in xcode è integrato nel pannello inferiore. Se si inserisce un numero nel terminale, verrà restituito un numero.

4

Questo post ha anche una buona spiegazione su come farlo usando il supporto del modulo di clang .

È strutturato in termini di come eseguire questa operazione per il progetto CommonCrypto, ma in generale dovrebbe funzionare per qualsiasi altra libreria C che si desidera utilizzare all'interno di Swift.

Ho brevemente provato a farlo per zlib. Ho creato un nuovo progetto di framework iOS e creato una directory zlib, contenente un file module.modulemap con il seguente:

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

Quindi in Target -> Collega binario con librerie ho selezionato aggiungi elementi e aggiunto libz.tbd.

Potresti voler costruire a questo punto.

Sono stato quindi in grado di scrivere il seguente codice:

import zlib

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

Non è necessario mettere il nome della libreria zlib in primo piano, tranne che nel caso precedente ho chiamato la funzione Swift class come la funzione C, e senza la qualifica la funzione Swift finisce per essere chiamata ripetutamente fino a quando l'applicazione non si ferma.


3

Nel caso di c ++, si verifica questo errore:

  "_getInput", referenced from: 

È necessario anche un file di intestazione c ++. Aggiungi c-linkage alla tua funzione, quindi includi il file header nell'intestazione bridge:

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"

Ecco il video originale che spiega questo


se non funziona, prova ad aggiungere un __OBJCcontrollo al tuo Bridging-Header, ad es#ifdef __OBJC @import UIKit; #endif
Chris Yim

1

Sembra essere una palla piuttosto diversa quando si tratta di puntatori. Ecco cosa ho finora per chiamare la readchiamata di sistema 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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.