【问题标题】:String.withCString when the String is nilString.withCString 当 String 为 nil 时
【发布时间】:2017-05-19 09:02:54
【问题描述】:

将要描述的问题与我之前的问题有关: string.withCString and UnsafeMutablePointer(mutating: cstring) wrapped into a function 这是我处理 nil 字符串的第一种方法(通过将 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 或pin 将是无效的(但它实际上是有效的,我不知道为什么?!)。

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

所以我尝试让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 代码。

【问题讨论】:

    标签: c swift interop unsafe-pointers


    【解决方案1】:

    问题

    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.

    【讨论】:

    • 好的,首先,我非常感谢您通过链接、示例和解释为您的答案付出的所有巨大努力和工作。所以在实践中,这意味着只有你提到的理论方法(将其更改为 const char*)由于这个错误而不起作用?但是上面的扩展会起作用吗?扩展被标记为红色:“相同类型的要求使泛型参数 'Wrapped' 非泛型”:/
    • @n.eesemann:它在我的 Xcode 8.3.2 (Swift 3.1) 中编译。 – 是的,最后一种方法应该有效,但它目前没有多个可选字符串,如该错误中所述。
    • 刚刚更新了 Xcode 和 swift,现在你建议的扩展代码也在我的机器上运行了 :) 。一百万谢谢,我会赞成你的回答,但我缺少 4 个声望点!
    猜你喜欢
    • 2011-07-01
    • 1970-01-01
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多