为什么Swift在将可选String转换为UnsafePointer时返回一个意外的指针?

时间:2016-10-01 13:09:30

标签: swift

我在使用C库时注意到了一些不寻常的行为,这个C库将字符串作为const char *(将其转换为Swift为UnsafePointer<Int8>!);传递String按预期工作,但String?似乎破坏了输入。考虑一下我写的测试:

func test(_ input: UnsafePointer<UInt8>?) {
    if let string = input {
        print(string[0], string[1], string[2], string[3], string[4], string[5])
    } else {
        print("nil")
    }
}

let input: String = "Hello"

test(input)

这按预期工作,为输入字符串打印以空值终止的UTF-8字节列表:72 101 108 108 111 0

但是,如果我将输入更改为可选字符串,那么它将变为:

let input: String? = "Hello"

我在结果(176 39 78 23 1 0)中得到了一组完全不同的值,即使我希望它是相同的。传入nil按预期工作。

C库的函数允许NULL代替字符串,我有时也希望在Swift中传递它,因此输入字符串是可选的。< / p>

这是Swift中的错误,还是Swift没有设计来处理这种情况?无论哪种方式,处理这种情况的最佳方式是什么?

修改

它似乎与多个参数有关。 C函数:

void multiString(const char *arg0, const char *arg1, const char *arg2, const char *arg3) {
    printf("%p: %c %c %c\n", arg0, arg0[0], arg0[1], arg0[2]);
    printf("%p: %c %c %c\n", arg1, arg1[0], arg1[1], arg1[2]);
    printf("%p: %c %c %c\n", arg2, arg2[0], arg2[1], arg2[2]);
    printf("%p: %c %c %c\n", arg3, arg3[0], arg3[1], arg3[2]);
}

夫特:

let input0: String? = "Zero"
let input1: String? = "One"
let input2: String? = "Two"
let input3: String? = "Three"

multiString(input0, input1, input2, input3)

结果:

0x101003170: T h r
0x101003170: T h r
0x101003170: T h r
0x101003170: T h r

似乎有一个关于Swift如何处理多个参数的错误。

3 个答案:

答案 0 :(得分:1)

如果这是期望的行为或只是一个错误,我没有找到任何有用的东西。

实用的解决方案可能只是拥有这样的代理方法,但你可能已经做了类似的事情。

func proxy(_ str: String?, _ functionToProxy: (UnsafePointer<UInt8>?) -> ()) {
    if let str = str {
        functionToProxy(str)
    } else {
        functionToProxy(nil)
    }
}

proxy(input, test)

您是否测试过它是否在Swift 2中运行?他们在Swift 3中改变了可能相关的东西:

https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md

答案 1 :(得分:0)

需要明确的是,在Apple修复此问题之前,有一个解决方法。在传递它们之前打开你的可选字符串,一切都会正常工作。

var anOptional: String?
var anotherOptional: String?

func mySwiftFunc() {

    let unwrappedA = anOptional!
    let unwrappedB = anotherOptional!

    myCStringFunc(unwrappedA, unwrappedB)

}

答案 2 :(得分:0)

正如评论中所提到的,这是Swift中的一个明显错误。

这是我正在使用的解决方法。如果您不能信任Swift将字符串转换为指针,那么您必须自己完成。

假设C函数定义为:

void multiString(const char *arg0, const char *arg1, const char *arg2);

Swift代码:

func callCFunction(arg0: String?, arg1: String?, arg2: String?) {
    let dArg0 = arg0?.data(using: .utf8) as NSData?
    let pArg0 = dArg0?.bytes.assumingMemoryBound(to: Int8.self)

    let dArg1 = arg1?.data(using: .utf8) as NSData?
    let pArg1 = dArg1?.bytes.assumingMemoryBound(to: Int8.self)

    let dArg2 = arg2?.data(using: .utf8) as NSData?
    let pArg2 = dArg2?.bytes.assumingMemoryBound(to: Int8.self)

    multiString(pArg1, pArg2, pArg3)
}

警告:

不要试图把它放在像这样的函数中:

/* DO NOT USE -- BAD CODE */
func ocstr(_ str: String?) -> UnsafePointer<Int8>? {
    guard let str = str else {
        return nil
    }

    let nsd = str.data(using: .utf8)! as NSData

    //This pointer is invalid on return:
    return nsd.bytes.assumingMemoryBound(to: Int8.self)
}

将删除重复的代码。这不起作用,因为数据对象nsd在函数末尾被释放。因此,指针在返回时无效。