Як зателефонувати на C від Swift?


136

Чи є спосіб зателефонувати на підпрограми C від Swift?

Дуже багато бібліотек iOS / Apple - це лише C, і я все одно хотів би їх викликати.

Наприклад, я хотів би отримати можливість швидко зателефонувати в бібліотеки виконання програми objc.

Зокрема, як ви з'єднуєте заголовки iOS C?

Відповіді:


106

Так, ви, звичайно, можете взаємодіяти з бібліотеками Apples C. Ось пояснено, як.
В основному типи C, покажчики C тощо переводяться на об'єкти Swift, наприклад, C intу Swift є a CInt.

Я буду буду крихітний приклад для іншого питання, яке можна використати як невелике пояснення, як перейти між C і 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);

Ось оригінальна відповідь.


1
Я новачок у ios / swift. Я хотів би використовувати c-функцію реєстрації з #include <asl.h> у швидких файлах. Хтось?
Дмитро Коновалов

чи сумісний він із файлами C ++, .cpp ??
Карлос.V

Щоб безпосередньо використовувати файли C ++, слід створити обгортку з об'єктивною метою C. Ви хочете, щоб реалізація (яка повинна знаходитись у файлі .mm) містити код Objective-C ++ (який є лише Objective-C і C ++ в одному файлі) та інтерфейс (який повинен бути власним .h файл заголовка) повинен містити чистий код Objective-C, тому вам доведеться конвертувати типи C ++ у типи Objective-C у реалізації та піддавати їх Swift. Тоді ви можете імпортувати цей заголовок за допомогою заголовка об'єднаного об'єктива-c.
Вільям Т Фроггард

2
"Ви, звичайно, можете взаємодіяти з бібліотеками Apples C" Неправильно. Ви можете взаємодіяти з будь-якими бібліотеками С, зовсім не обмежуючись лише Apple.
Сліп Д. Томпсон

Оновлено посилання на документацію для developer.apple.com/documentation/swift/…
MechEthan

9

Компілятор перетворює API C у 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 у документах.


1
Дякую за приклад, і ви маєте рацію, я бачу, як це могло б працювати. Однак насправді це не відповідає на моє запитання, яке було викликати яблуко C бібліотеки. Але це, безумовно, найближче.
Блейз

Подивіться в іншому документі. Наприклад, "об'єкти" ( CFTypeRef) у стилі CoreFoundation перетворюються на об'єкти Swift. Більшість функцій ObjCRuntime.h, однак, не мають сенсу для Swift.
рикстер

"Більшість функцій ObjCRuntime.h не мають сенсу для Swift, хоча" Чому ви це говорите? Я думаю, що мені потрібно знайти спосіб імпортувати заголовок і переадресувати його. Це здається незручним, але я думаю, що це шлях.
Блейз

5

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

  1. Файл-> Створити-> Проект
  2. виберіть інструмент командного рядка як попередньо заданий проект та назвіть проект "cliinput"
  3. клацніть правою кнопкою миші в навігаторі проекту (синя панель зліва) та виберіть "Новий файл ..."
  4. У діалоговому вікні, що випадає, назвіть файл "UserInput". Зніміть прапорець "Також створити файл заголовка". Після натискання кнопки «Далі» у вас з’явиться запитання, чи повинен XCode створити для вас файл Bridging-Header.h. Виберіть "Так".
  5. Скопіюйте та вставте код з відповіді Леандро вище. Після натискання на кнопку відтворення вона повинна компілюватися і запускатися в терміналі, який в 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 таким же, як і функцію C, і без кваліфікації функція Swift закінчується повторним викликом, поки програма не зупиниться.


3

У випадку c ++ виникає ця помилка:

  "_getInput", referenced from: 

Вам також потрібен файл заголовка c ++. Додайте c-зв'язок до своєї функції, а потім включіть заголовок у міст-заголовок:

Швидкий 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);
}    

головна

import Foundation
var output: CInt = 0
getInput(&output)
print(output)

cliinput-Bridging-Header.h

#include "UserInput.h"

Ось оригінальне відео, що пояснює це


якщо це не працює, спробуйте додати __OBJCчек до #ifdef __OBJC @import UIKit; #endif
Chris

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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.