String为nil

时间:2017-05-19 09:02:54

标签: c swift interop unsafe-pointers

将要描述的问题与我之前的问题有关: string.withCString and UnsafeMutablePointer(mutating: cstring) wrapped into a function这是我处理nil Strings的第一种方法(通过将withCString放入函数中)  以及Mecki问的一个问题: Why can't I pass an optional Swift String to C function that allows NULL pointers?

想象一下有一个c函数,如:

unsigned long randomSign(char *pin, char *tag_signature, char *tag_data, char *xyz);

我知道如果我在相应的swift函数周围包装4个string.withCString闭包,该函数可以正常工作:

// pin, tag_signature, tag_data and xyz are optional Strings so they may be nil which is a problem for my result.
// corresponding swift function:
// randomSign(pin: UnsafeMutablePointer<Int8>!, tag_signature: UnsafeMutablePointer<Int8>!, tag_data: UnsafeMutablePointer<Int8>!, xyz: UnsafeMutablePointer<Int8>!)
let result = pin.withCString { s1 in return
    tag_signature.withCString {s2 in return
        tag_data.withCString {s3 in return
            xyz.withCString { s4 in return 
                randomSign(UnsafeMutablePointer(mutating: s1), UnsafeMutablePointer(mutating: s2), UnsafeMutablePointer(mutating: s3), UnsafeMutablePointer(mutating: s4))
    }}}}

And so Martin R replied to an easier example,不需要在randomSign(arguments)和UnsafeMutablePointer(mutating:...)周围包装闭包,因为它也可以接受字符串并转换它。 但是当我删除闭包并将其用作Martin R described时,它在启动mac后直接在模拟器上启动时工作,但是在randomSign-Function的连续调用中,返回会告诉我例如tag_signature或者引脚无效(但它实际上是有效的,我不知道为什么?!)。

这引出了我需要withCString闭包的问题(目前)但我必须处理nil-Strings,这会导致应用程序在返回结果时崩溃,因为它无法评估randomSign-Function。

所以我试图让the approach below (also suggested by @Martin R)适合Swift 3,但我没有适应它来锻炼。

//Swift-2 written by Martin R
protocol CStringConvertible {
    func withCString<Result>(@noescape f: UnsafePointer<Int8> throws -> Result) rethrows -> Result
}

extension String: CStringConvertible { }

extension Optional where Wrapped: CStringConvertible {
    func withOptionalCString<Result>(@noescape f: UnsafePointer<Int8> -> Result) -> Result {
        if let string = self {
            return string.withCString(f)
        } else {
            return f(nil)
        }
    }
}

//Swift 3: ???

如果有人能告诉我,为什么我的功能只有在我使用withCString时才能解决,但是当我解除它时,我会非常感激 如果有人知道如何解决这个问题,即正确地将swift-2代码翻译成工作的swift-3代码。

1 个答案:

答案 0 :(得分:0)

的问题
let result = randomSign(UnsafeMutablePointer(mutating: pin),
                    UnsafeMutablePointer(mutating: tag_signature),
                    UnsafeMutablePointer(mutating: tag_data),
                    UnsafeMutablePointer(mutating: xyz))

是从Swift创建的临时UTF-8表示 字符串仅在UnsafeMutablePointer()的每次调用期间有效, 但在调用randomSign()期间不一定有效。 (所以我在https://stackoverflow.com/a/44027397/1187415的最终建议 实际上不正确,我已经更新了那部分。)

https://stackoverflow.com/a/39363769/1187415中包装器的可能Swift 3版本是

extension Optional where Wrapped == String {
    func withOptionalCString<Result>(_ f: (UnsafeMutablePointer<Int8>?) -> Result) -> Result {
        if let string = self {
            return string.withCString { f(UnsafeMutablePointer(mutating: $0)) }
        } else {
            return f(nil)
        }
    }
}

它处理选项性强制转换C字符串指针 到一个可变指针(根据randomSign()的要求)。这可以 叫做

let result = pin.withOptionalCString { s1 in
    tag_signature.withOptionalCString { s2 in
        tag_data.withOptionalCString { s3 in
            xyz.withOptionalCString { s4 in
                randomSign(s1, s2, s3, s4)
            }
        }
    }
}

备注:理论上,如果randomSign()的签名更改为采用const char *参数,则可以避免此问题:

unsigned long randomSign(const char *pin, const char *tag_signature, const char *tag_data, const char *xyz);

然后可以简单地将其称为

let result = randomSign(pin, tag_signature, tag_data, xyz)

带有可选或非可选的Swift字符串。 但是,正如报道的那样,这目前无效 SR-2814 Swift does not correctly pass in multiple optional strings to C function