我在使用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如何处理多个参数的错误。
答案 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
在函数末尾被释放。因此,指针在返回时无效。