Как позвонить C из Swift?

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

Многие библиотеки iOS / Apple предназначены только для языка Си, и я все еще хотел бы иметь возможность вызывать их.

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

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

6 ответов

Решение

Да, вы, конечно, можете взаимодействовать с библиотеками Apple's C. Здесь объясняется как.
В основном, типы C, указатели C и т. Д. Переводятся в объекты Swift, например C int в Свифте 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-Bridging-header.h

void getInput(int *output);

Вот оригинальный ответ.

Компилятор преобразует 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)
}

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

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

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

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

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

Я кратко экспериментировал с этим для zlib. Я создал новый проект iOS Framework и создал каталог 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 *
}

Затем в Targets -> Link Binary With Libraries я выбрал add items и добавил libz.tbd.

Вы можете построить на этом этапе.

Затем я смог написать следующий код:

import zlib

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

Вам не нужно помещать имя библиотеки zlib впереди, за исключением того, что в приведенном выше случае я назвал функцию класса Swift такой же, как и функцию C, и без квалификации функция Swift будет вызываться повторно, пока приложение не остановится.

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

  "_getInput", referenced from: 

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

Свифт 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"

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

Похоже, что при работе с указателями это совсем другой шар. Вот то, что я пока называю C POSIX read системный вызов:

enum FileReadableStreamError : Error {
case failedOnRead
}

// Some help from: http://stackru.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
}
Другие вопросы по тегам