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

136

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

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

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

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

полыхать
источник

Ответы:

106

Да, вы, конечно, можете взаимодействовать с библиотеками 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-Bridging-header.h

void getInput(int *output);

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

Leandros
источник
1
Я новичок в IOS / Swift. Я хотел бы использовать функцию регистрации c из #include <asl.h> в файлах swift. Кто угодно?
Дмитрий Коновалов
это совместимо с C ++, .cpp файлами ??
Carlos.V
Чтобы использовать файлы C ++ напрямую, вы должны создать оболочку Objective-C. Вы захотите, чтобы реализация (которая должна постоянно находиться в файле .mm) содержала код Objective-C ++ (который является просто Objective-C и C ++ в одном файле) и интерфейс (который должен быть в своем собственном .h файл заголовка) должен содержать чистый код Objective-C, поэтому вам придется преобразовывать типы C ++ в типы Objective-C в реализации, чтобы представить их Swift. Затем вы можете импортировать этот заголовок с помощью соединительного заголовка target-c.
Уильям Т Фроггард
2
«Конечно, вы можете взаимодействовать с библиотеками Apples C». Неправильно. Вы можете взаимодействовать с любыми библиотеками Си, не ограничиваясь только Apple.
Слипп Д. Томпсон
Обновлена документация ссылка developer.apple.com/documentation/swift/...
MechEthan
9

Компилятор преобразует 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 в документации.

rickster
источник
1
Спасибо за пример, и вы правы, я вижу, как это может работать. Однако на самом деле это не отвечает на мой вопрос, который состоял в том, как вызывать библиотеки Apple C. Но это определенно ближе всего.
Blaze
Посмотрите в другом месте в этом документе. Например, «объекты» ( CFTypeRef) в стиле CoreFoundation преобразуются в объекты Swift. Однако большинство функций ObjCRuntime.h не имеют смысла для Swift.
Рикстер
«Большинство функций ObjCRuntime.h не имеют никакого значения для Swift». Почему вы это говорите? Я думаю, что мне нужно найти способ импортировать заголовок и соединить его. Это кажется неловким, но я полагаю, что это путь.
Blaze
5

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

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

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

юлианский
источник
3

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

  "_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"

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

Джон Джеймс
источник
если это не сработает, попробуйте добавить __OBJCпроверку к своему Bridging-Header, например#ifdef __OBJC @import UIKit; #endif
Крис Йим
1

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