จะโทร C จาก Swift ได้อย่างไร


138

มีวิธีเรียก 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);

นี่คือคำตอบเดิม


1
ฉันยังใหม่กับ ios / swift ฉันต้องการใช้การบันทึก c-function จาก #include <asl.h> ในไฟล์ที่รวดเร็ว ทุกคน?
Dmitry Konovalov

มันเข้ากันได้กับไฟล์ C ++, .cpp ??
Carlos.V

ในการใช้ไฟล์ C ++ โดยตรงคุณควรสร้างตัวห่อวัตถุประสงค์ -C คุณจะต้องการให้การนำไปใช้งาน (ซึ่งควรอยู่ในไฟล์. mm) มีโค้ด Objective-C ++ (ซึ่งเป็นเพียง Objective-C และ C ++ ในไฟล์เดียว) และอินเทอร์เฟซ (ซึ่งควรอยู่ในไฟล์. h ไฟล์ส่วนหัว) ควรมีรหัส Objective-C ที่บริสุทธิ์ดังนั้นคุณจะต้องแปลงประเภท C ++ เป็นประเภท Objective-C ในการใช้งานเพื่อแสดงให้ Swift เห็น จากนั้นคุณสามารถนำเข้าส่วนหัวนั้นโดยมีวัตถุประสงค์ -c bridging header
William T Froggard

2
“ แน่นอนว่าคุณสามารถโต้ตอบกับห้องสมุด Apples C ได้” ไม่ถูกต้อง คุณสามารถโต้ตอบกับไลบรารี C ใดก็ได้ไม่ จำกัด เฉพาะของ Apple เท่านั้น
Slipp D.Thompson

อัปเดตลิงค์เอกสาร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ในเอกสาร


1
ขอบคุณสำหรับตัวอย่างและคุณพูดถูกฉันเห็นว่ามันได้ผลอย่างไร อย่างไรก็ตามมันไม่ได้ตอบคำถามของฉันจริงๆซึ่งเป็นวิธีเรียกไลบรารีของ apple C แต่มันใกล้เคียงที่สุดแน่นอน
Blaze

ดูที่อื่นในเอกสารนั้น ตัวอย่างเช่น "วัตถุ" ( CFTypeRef) สไตล์ CoreFoundation จะถูกแปลงเป็นวัตถุ Swift แม้ว่าฟังก์ชัน ObjCRuntime.h ส่วนใหญ่จะไม่มีความหมายกับ Swift
rickster

"ฟังก์ชัน ObjCRuntime.h ส่วนใหญ่ไม่มีความหมายกับ Swift" ทำไมคุณถึงพูดแบบนั้น? ฉันคิดว่าฉันต้องหาวิธีนำเข้าส่วนหัวและเชื่อมโยง มันดูน่าอึดอัด แต่ฉันคิดว่ามันเป็นหนทางที่จะไป
Blaze

5

ในกรณีที่คุณยังใหม่กับ XCode เหมือนกับฉันและต้องการลองใช้ตัวอย่างที่โพสต์ไว้ในคำตอบของ Leandro :

  1. ไฟล์ -> ใหม่ -> โครงการ
  2. เลือก Command Line Tool เป็นโปรเจ็กต์ที่ตั้งไว้และตั้งชื่อโปรเจ็กต์ว่า "cliinput"
  3. คลิกขวาในตัวนำทางโครงการ (แผงสีฟ้าทางด้านซ้าย) แล้วเลือก "ไฟล์ใหม่ ... "
  4. ในกล่องโต้ตอบแบบเลื่อนลงชื่อไฟล์ "UserInput" ยกเลิกการเลือกช่อง "สร้างไฟล์ส่วนหัวด้วย" เมื่อคุณคลิก "ถัดไป" ระบบจะถามว่า XCode ควรสร้างไฟล์ Bridging-Header.h ให้คุณหรือไม่ เลือก "ใช่"
  5. คัดลอกและวางโค้ดจากคำตอบของ Leandro ด้านบน เมื่อคุณคลิกที่ปุ่มเล่นควรคอมไพล์และรันในเทอร์มินัลซึ่งใน xcode มีอยู่ในแผงด้านล่าง หากคุณป้อนหมายเลขในเทอร์มินัลหมายเลขจะถูกส่งกลับ

4

โพสต์นี้ยังมีคำอธิบายที่ดีเกี่ยวกับวิธีการทำเช่นนี้โดยใช้เสียงดังกราวของการสนับสนุนโมดูล

มีกรอบในแง่ของวิธีการทำสิ่งนี้สำหรับโครงการ 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 func จะถูกเรียกซ้ำ ๆ จนกว่าแอปพลิเคชันจะหยุดลง


3

ในกรณีของ c ++ มีข้อผิดพลาดนี้ปรากฏขึ้น:

  "_getInput", referenced from: 

คุณต้องมีไฟล์ส่วนหัว c ++ ด้วย เพิ่มc-linkageลงในฟังก์ชันของคุณจากนั้นรวมไฟล์ส่วนหัวไว้ใน bridge-header:

สวิฟต์ 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
Chris Yim

1

ดูเหมือนว่าจะเป็นลูกแว็กซ์ที่ค่อนข้างแตกต่างกันเมื่อจัดการกับพอยน์เตอร์ นี่คือสิ่งที่ฉันมีสำหรับการโทรระบบ 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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.