Как позвонить в С из Свифта?

Есть ли способ вызвать подпрограммы C из Swift?

Многие библиотеки iOS/Apple имеют только C, и я все равно хотел бы их назвать.

Например, я хотел бы иметь возможность вызывать библиотеки времени выполнения objc из swift.

В частности, как вы соединяете заголовки iOS C?

Ответ 1

Да, вы можете, конечно, взаимодействовать с библиотеками Apples C. Здесь объясняется как.

В основном, C-типы, C-указатели и т.д. Переводятся в объекты Swift, например C int в Swift - это 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-мост-header.h

void getInput(int *output);

Здесь - оригинальный ответ.

Ответ 2

Компилятор преобразует C API в Swift так же, как и для Objective-C.

import Cocoa

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

import Darwin

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

См. Взаимодействие с API Objective-C в документах.

Ответ 3

На всякий случай, вы так же новичок в XCode, как и я, и хотите попробовать фрагменты, опубликованные в Leandro answer:

  • File- > New- > Project
  • выберите Command Line Tool в качестве предустановки проекта и назовите проект cliinput
  • щелкните правой кнопкой мыши в навигаторе проекта (синяя панель слева) и выберите "Новый файл..."
  • В раскрывающемся диалоговом окне введите файл "UserInput". Снимите флажок "Также создайте файл заголовка". Когда вы нажмете "Далее", вас спросят, должен ли XCode создать файл Bridging-Header.h для вас. Выберите "Да".
  • Скопируйте и вставьте код из Leandro, чтобы ответить выше. Как только вы нажмете на кнопку воспроизведения, она должна скомпилироваться и запуститься в терминале, который в xcode встроен в нижнюю панель. Если вы введете номер в терминал, будет возвращен номер.

Ответ 4

Этот пост также содержит хорошее объяснение, как это сделать, используя clang поддержка модуля.

Он сформулирован с точки зрения того, как это сделать для проекта CommonCrypto, но в целом он должен работать для любой другой библиотеки C, которую вы хотите использовать из Swift.

Я кратко экспериментировал с этим для zlib. Я создал новый проект инфраструктуры iOS и создал каталог zlib, содержащий файл module.modulemap со следующим:

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 заканчивается, вызывается многократно, пока приложение привалы.

Ответ 5

Кажется, что это совсем другой шар, когда речь идет о указателях. Вот что я до сих пор вызывал системный вызов 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
}

Ответ 6

В случае С++ появляется эта ошибка:

  "_getInput", referenced from: 

Вам также нужен заголовочный файл С++. Добавьте c-linkage в свою функцию, затем включите заголовочный файл в заголовке моста:

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-мост-header.h

#include "UserInput.h"

Вот оригинальное видео, объясняющее это