将 Swift 字符串传递给 C

3

我对Swift还不太熟悉,对C的接触也不多。

我正在尝试编写一个在C中获取Swift字符串并进行处理的函数。问题是,我不确定在Swift中应该使用什么类型才能让C看到它时喜欢。

到目前为止,我在Stack上找到了几个好的起点示例,但有些示例似乎已经过时了。

一开始,我使用了Swift call C call Swift?这个例子让C和Swift相互交流。 然后,我尝试更新Swift函数以返回某种字符串。我知道它需要是UTF-8返回类型,但我不确定如何正确地发送它们。 我已经查看了How to pass a Swift string to a c function?How to convert String to UnsafePointer<UInt8> and lengthHow to convert string to unicode(UTF-8) string in Swift?,但它们都没有真正解决问题。 或者我只是输入不正确。到目前为止,我最接近返回结果的方法如下。

在Swift中,我的ViewController是:

import UIKit

class ViewController: UIViewController {

    @_silgen_name("mySwiftFunc") // give the function a C name
    public func mySwiftFunc(number: Int) -> [CChar]
    {
        print("Hello from Swift: \(number)")

        let address: String = "hello there";
        let newString = address.cString(using: String.Encoding.utf8)
        return newString!
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        blah()

    }
}

在 C 语言中,头文件的格式如下:

#ifndef cfile_h
#define cfile_h

#include <stdio.h>

const char * mySwiftFunc(int);
int blah(void);

#endif /* cfile_h */

源代码如下:

#include "cfile.h"

int blah() {

    const char * retVal = mySwiftFunc(42); // call swift function
    printf("Hello from C: %s", retVal);

    return 0;
}

有一个桥接头文件,只包含#include "cfile.h"。显然,第一个示例中仍然有许多残留物,这些将在以后清理。

为了使其工作,需要做出哪些更改?目前,控制台输出如下:

Hello from Swift: 42
Hello from C: (B\214

我现在没有时间去研究这个问题,但我必须说这是一个非常好的问题。 - Alexander
1个回答

7

在Swift中,const char *的等效类型是UnsafePointer<CChar>?,因此这是正确的返回值。然后你需要考虑内存管理。其中一个选项是在Swift函数中分配C字符串的内存,并将释放内存的责任留给调用者:

public func mySwiftFunc(number: Int) -> UnsafePointer<CChar>? {
    print("Hello from Swift: \(number)")

    let address = "hello there"
    let newString = strdup(address)
    return UnsafePointer(newString)
}

将一个Swift字符串传递给strdup()函数,自动创建一个(临时的)C字符串表示。然后这个C字符串被复制了一份。当不再需要这段内存时,调用的C函数必须释放它:
int blah() {
    const char *retVal = mySwiftFunc(42);
    printf("Hello from C: %s\n", retVal);
    free((char *)retVal);
    return 0;
}

⚠️ 但是: 请注意,你的代码中还存在更多问题:

  • mySwiftFunc() 是一个类的实例方法,因此具有隐式的 self 参数,该参数被调用的 C 函数忽略。这可能是偶然成功的,或者会导致奇怪的失败。
  • @_silgen_name 不应在 Swift 标准库之外使用,参见 Swift 论坛中的此讨论
  • 一个稍微更好的替代方案是 @_cdecl,但它甚至没有得到官方支持。@_cdecl 只能用于全局函数。
  • 通常情况下,直接从 C 中调用 Swift 函数是 不受官方支持的,请参见 Swift 论坛中的此讨论了解原因和可能的替代方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接