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.