Làm thế nào để gọi C từ Swift?


136

Có cách nào để gọi các thói quen C từ Swift không?

Rất nhiều thư viện iOS / Apple chỉ có C và tôi vẫn muốn gọi chúng.

Ví dụ, tôi muốn có thể gọi các thư viện thời gian chạy objc từ swift.

Cụ thể, làm thế nào để bạn kết nối các tiêu đề iOS C?

Câu trả lời:


106

Có, tất nhiên bạn có thể tương tác với các thư viện Táo C. Dưới đây là giải thích như thế nào.
Về cơ bản, các loại C, con trỏ C, v.v ... được dịch thành các đối tượng Swift, ví dụ: C inttrong Swift là a CInt.

Tôi đã xây dựng một ví dụ nhỏ, cho một câu hỏi khác, có thể được sử dụng như một lời giải thích nhỏ, về cách kết nối giữa C và Swift:

chính

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);

Đây là câu trả lời ban đầu.


1
Tôi mới sử dụng ios / swift. Tôi muốn sử dụng chức năng ghi nhật ký từ #include <asl.h> trong các tệp swift. Bất kỳ ai?
Dmitry Konovalov

nó có tương thích với các tập tin C ++, .cpp không ??
Carlos.V

Để sử dụng trực tiếp các tệp C ++, bạn nên tạo một trình bao bọc C-object. Bạn sẽ muốn việc triển khai (sẽ nằm trong tệp .mm) chứa mã Objective-C ++ (chỉ là Objective-C và C ++ trong một tệp) và giao diện (phải ở trong .h tệp tiêu đề) phải chứa mã Objective-C thuần túy, vì vậy bạn sẽ phải chuyển đổi các loại C ++ thành các loại Objective-C khi triển khai, để hiển thị chúng thành Swift. Sau đó, bạn có thể nhập tiêu đề đó với tiêu đề bắc cầu khách quan.
William T Froggard

2
Tất nhiên, bạn có thể tương tác với các thư viện Táo C Không đúng. Bạn có thể tương tác với bất kỳ thư viện C nào , không giới hạn ở tất cả chỉ giới hạn ở Apple.
Slipp D. Thompson

Cập nhật tài liệu liên kết developer.apple.com/documentation/swift/ từ
MechEthan

9

Trình biên dịch chuyển đổi API C thành Swift giống như đối với Objective-C.

import Cocoa

let frame = CGRect(x: 10, y: 10, width: 100, height: 100)

import Darwin

for _ in 1..10 {
    println(rand() % 100)
}

Xem Tương tác với API khách quan-C trong tài liệu.


1
Cảm ơn ví dụ này, và bạn đã đúng, tôi có thể thấy nó có thể hoạt động như thế nào. Tuy nhiên, nó không thực sự trả lời câu hỏi của tôi, đó là cách gọi thư viện apple C. Nhưng nó chắc chắn là gần nhất.
Blaze

Tìm nơi khác trong tài liệu đó. Ví dụ: "đối tượng" kiểu CoreFoundation CFTypeRefđược chuyển đổi thành đối tượng Swift. Mặc dù vậy, hầu hết các hàm ObjCR nb.h không có ý nghĩa đối với Swift.
gà trống

"Hầu hết các hàm ObjCR nb.h không có ý nghĩa đối với Swift," Tại sao bạn lại nói vậy? Tôi nghĩ rằng tôi cần phải tìm cách nhập một tiêu đề và bắc cầu cho nó. Điều đó có vẻ khó xử, nhưng tôi cho rằng đó là cách để đi.
Blaze

5

Chỉ trong trường hợp bạn là người mới với XCode như tôi và muốn thử các đoạn được đăng trong câu trả lời của Leandro :

  1. Tệp-> Mới-> Dự án
  2. chọn Command Line Tool làm dự án đặt trước và đặt tên cho dự án là "cliinput"
  3. nhấp chuột phải vào trình điều hướng dự án (bảng màu xanh bên trái) và chọn "Tệp mới ..."
  4. Trong hộp thoại thả xuống tên tệp "UserInput". Bỏ chọn hộp "Đồng thời tạo tệp tiêu đề". Khi bạn nhấp vào "Tiếp theo", bạn sẽ được hỏi liệu XCode có nên tạo tệp Bridging-Header.h cho bạn không. Chọn "Có".
  5. Sao chép và dán mã từ câu trả lời của Leandro ở trên. Khi bạn nhấp vào nút phát, nó sẽ biên dịch và chạy trong thiết bị đầu cuối, trong xcode được tích hợp trong bảng điều khiển dưới cùng. Nếu bạn nhập một số trong thiết bị đầu cuối, một số sẽ được trả lại.

4

Bài đăng này cũng có một lời giải thích tốt về cách thực hiện điều này bằng cách sử dụng hỗ trợ mô-đun của clang .

Nó đóng khung về cách thực hiện điều này cho dự án CommonCrypto, nhưng nói chung, nó sẽ hoạt động cho bất kỳ thư viện C nào khác mà bạn muốn sử dụng từ trong Swift.

Tôi đã thử nghiệm ngắn gọn với việc làm điều này cho zlib. Tôi đã tạo một dự án khung iOS mới và tạo một thư mục zlib, chứa tệp module.modulemap với các mục sau:

module zlib [system] [extern_c] {
    header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/zlib.h"
    export *
}

Sau đó, trong mục tiêu -> Liên kết nhị phân với thư viện tôi đã chọn thêm các mục và thêm libz.tbd.

Bạn có thể muốn xây dựng tại thời điểm này.

Sau đó tôi đã có thể viết mã sau đây:

import zlib

public class Zlib {
    public class func zlibCompileFlags() -> UInt {
        return zlib.zlibCompileFlags()
    }
}

Bạn không cần phải đặt tên thư viện zlib ở phía trước, ngoại trừ trong trường hợp trên tôi đã đặt tên cho lớp Swift giống như hàm C và không có đủ điều kiện, func Swift kết thúc được gọi liên tục cho đến khi ứng dụng dừng lại.


3

Trong trường hợp của c ++, có lỗi này bật lên:

  "_getInput", referenced from: 

Bạn cũng cần một tệp tiêu đề c ++. Thêm liên kết c vào chức năng của bạn, sau đó đưa tệp tiêu đề vào tiêu đề cầu nối:

Swift 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);
}    

chính

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

cliinput-Bridging-Header.h

#include "UserInput.h"

Đây là video gốc giải thích điều này


nếu nó không làm việc, cố gắng thêm __OBJCséc cho bạn Bridging-Header, ví dụ:#ifdef __OBJC @import UIKit; #endif
Chris Yim

1

Nó dường như là một quả bóng khá khác biệt khi tiếp xúc với con trỏ. Đây là những gì tôi có cho đến nay để gọi cuộc gọi readhệ thống 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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.