本周我刚开始使用Swift,特别是Swift 4,我通过桥接头liblo使用C库,它处理发送/接收OSC(开放声音控制)格式的消息通过网络套接字。
我能够启动服务器线程,通过C回调接收OSC消息 - > Swift闭包,并使用Swift读取数值参数值就好了。但是,我在阅读字符串值时遇到了麻烦。
liblo消息参数类型lo_arg is a C typedef for a union和字符串参数类型被声明为简单字符,它们作为Int8映射到Swift。
在C中,您可以从回调的&argv[i]->s
数组中通过lo_arg **argv
获取字符串。在使用liblo的Obj-C项目中,我使用:
// get string value of first argument
lo_arg arg = argv[0];
NSString *s = [NSString stringWithUTF8String:&arg->s];
// do something with s
在Swift中,我尝试获取Int8的地址并将其提供给有效的字符串,但只抓取第一个字符:
// get string value of first argument
if var arg : lo_arg = argv?.pointee![0] {
withUnsafePointer(to: &arg.s) {
let s = String(cString: $0)
// so something with s
}
}
我做错了吗?我认为这些是等价的,但是将$0
传递给strlen()
ala print("strlen: \(strlen($0)")
只会打印长度为" 1"。我已经验证了多字符字符串确实是使用非Swift测试程序发送的。我现在想知道Swift是否以某种方式假设字符串是单个字符而不是C字符串头地址和/或我需要进一步指针转换。
答案 0 :(得分:0)
经过一番挖掘,我可以确认Swift截断了lo_arg->s
&我的64位系统上的lo_arg->S
字符串值为8个字节,即sizeof(char)
。当尝试从来自Swift 的的lo_arg中读取字符串时会发生这种情况。在C 中读取相同的值工作正常,因此Swift似乎保留/仅允许从单个字符的空间读取。将lo_arg从Swift转发到C并通过printf()
打印字符串也会显示最多8个字符的截断字符串。
快速解决方法是避免从Swift生成的lo_arg中读取字符串,从C中的原始lo_message中获取lo_arg,然后转换char"指针"一个const char * Swift将理解为一个可变长度的字符串。以下是我添加到桥接标题中的一些工作实用程序函数:
/// return an lo_message argv[i]->s in a format Swift can understand as a String
const char* lo_message_get_string(lo_message message, int at) {
return (const char *)&lo_message_get_argv(message)[at]->s;
}
/// return an lo_message argv[i]->S in a format Swift can understand as a String
const char* lo_message_get_symbol(lo_message message, int at) {
return (const char *)&lo_message_get_argv(message)[at]->S;
}
在Swift中,我可以转换为String:
let s = String(cString: lo_message_get_string(msg, 0))
// do something with s