Как позвонить 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, как и я, и хотите попробовать фрагменты, опубликованные в ответе Леандро:
- File-> New-> Project
- выберите инструмент командной строки в качестве предустановки проекта и назовите проект "cliinput"
- щелкните правой кнопкой мыши в навигаторе проекта (синяя панель слева) и выберите "Новый файл..."
- В раскрывающемся диалоговом окне укажите имя файла "UserInput". Снимите флажок "Также создать заголовочный файл". Как только вы нажмете "Далее", вас спросят, должен ли XCode создать файл Bridging-Header.h для вас. Выберите "Да".
- Скопируйте и вставьте код из ответа Леандро выше. Как только вы нажмете на кнопку воспроизведения, он должен скомпилироваться и запустить в терминале, который в 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
}