มีวิธีเรียก C รูทีนจาก Swift หรือไม่?
ไลบรารี iOS / Apple จำนวนมากเป็นแบบ C เท่านั้นและฉันยังต้องการโทรหาสิ่งเหล่านี้
ตัวอย่างเช่นฉันต้องการเรียกไลบรารีรันไทม์ objc จาก swift
โดยเฉพาะอย่างยิ่งคุณเชื่อมโยงส่วนหัว iOS C ได้อย่างไร
มีวิธีเรียก C รูทีนจาก Swift หรือไม่?
ไลบรารี iOS / Apple จำนวนมากเป็นแบบ C เท่านั้นและฉันยังต้องการโทรหาสิ่งเหล่านี้
ตัวอย่างเช่นฉันต้องการเรียกไลบรารีรันไทม์ objc จาก swift
โดยเฉพาะอย่างยิ่งคุณเชื่อมโยงส่วนหัว iOS C ได้อย่างไร
คำตอบ:
ได้แน่นอนคุณสามารถโต้ตอบกับห้องสมุด 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);
นี่คือคำตอบเดิม
คอมไพเลอร์แปลง 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ในเอกสาร
CFTypeRef
) สไตล์ CoreFoundation จะถูกแปลงเป็นวัตถุ Swift แม้ว่าฟังก์ชัน ObjCRuntime.h ส่วนใหญ่จะไม่มีความหมายกับ Swift
ในกรณีที่คุณยังใหม่กับ XCode เหมือนกับฉันและต้องการลองใช้ตัวอย่างที่โพสต์ไว้ในคำตอบของ Leandro :
โพสต์นี้ยังมีคำอธิบายที่ดีเกี่ยวกับวิธีการทำเช่นนี้โดยใช้เสียงดังกราวของการสนับสนุนโมดูล
มีกรอบในแง่ของวิธีการทำสิ่งนี้สำหรับโครงการ 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 จะถูกเรียกซ้ำ ๆ จนกว่าแอปพลิเคชันจะหยุดลง
ในกรณีของ 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
ดูเหมือนว่าจะเป็นลูกแว็กซ์ที่ค่อนข้างแตกต่างกันเมื่อจัดการกับพอยน์เตอร์ นี่คือสิ่งที่ฉันมีสำหรับการโทรระบบ 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
}