Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/119.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 来自导入的无符号字符2D数组的Swift字符串_Ios_C_Swift - Fatal编程技术网

Ios 来自导入的无符号字符2D数组的Swift字符串

Ios 来自导入的无符号字符2D数组的Swift字符串,ios,c,swift,Ios,C,Swift,我在iOS应用程序中使用第三方C库,我正在将其从Objective-C转换为Swift。我在尝试读取Swift中C库返回的一个结构时遇到了障碍 结构与此类似: typedef unsigned int LibUint; typedef unsigned char LibUint8; typedef struct RequestConfiguration_ { LibUint8 names[30][128]; LibUint numberNames; LibUint cu

我在iOS应用程序中使用第三方C库,我正在将其从Objective-C转换为Swift。我在尝试读取Swift中C库返回的一个结构时遇到了障碍

结构与此类似:

typedef unsigned int LibUint;
typedef unsigned char LibUint8;

typedef struct RequestConfiguration_ {
    LibUint8 names[30][128];
    LibUint numberNames;
    LibUint currentName;
} RequestConfiguration;
它作为包含128个LibUint8值的30个元组的元组导入Swift。在使用nested
with unsafepointer
调用进行了长时间的反复尝试之后,我最终开始寻找在Swift中迭代元组的解决方案

我最终使用的是以下功能:

/**
 * Perform iterator on every children of the type using reflection
 */
func iterateChildren<T>(reflectable: T, @noescape iterator: (String?, Any) -> Void) {
    let mirror = Mirror(reflecting: reflectable)
    for i in mirror.children {
        iterator(i.label, i.value)
    }
}

/**
 * Returns a String containing the characters within the Tuple
 */
func libUint8TupleToString<T>(tuple: T) -> String {
    var result = [CChar]()
    let mirror = Mirror(reflecting: tuple)
    for child in mirror.children {
        let char = CChar(child.value as! LibUint8)
        result.append(char)

        // Null reached, skip the rest.
        if char == 0 {
            break;
        }
    }

    // Always null terminate; faster than checking if last is null.
    result.append(CChar(0))

    return String.fromCString(result) ?? ""
}

/**
 * Returns an array of Strings by decoding characters within the Tuple
 */
func libUint8StringsInTuple<T>(tuple: T, length: Int = 0) -> [String] {
    var idx = 0
    var strings = [String]()
    iterateChildren(tuple) { (label, value) in
        guard length > 0 && idx < length else { return }

        let str = libUint8TupleToString(value)
        strings.append(str)
        idx++
    }
    return strings
}
我的解决方案使用反射来迭代第一个元组,使用反射来迭代第二个元组,因为我在对嵌套元组使用
withUnsafePointer
时得到了错误的字符串,我认为这是由于标记。当然,一定有一种方法可以读取数组中的C字符串,使用
UnsafePointer
类似
withUsafePointer(&struct.cstring){String.fromCString(UnsafePointer($0))}


为了清楚起见,我正在寻找用Swift读取这些C字符串的最快方法,即使这涉及到使用反射。

这里有一个可能的解决方案:

func handleConfiguration(var config: RequestConfiguration) {
    let numStrings = Int(config.numberNames)
    let lenStrings = sizeofValue(config.names.0)

    let names = (0 ..< numStrings).map { idx in 
        withUnsafePointer(&config.names) {
            String.fromCString(UnsafePointer<CChar>($0) + idx * lenStrings) ?? ""
        }
    }
    let currentName = names[Int(config.currentName)]

    print(names, currentName)
}
内存中有30*128个连续字节
withUnsafePointer(&config.names)
使用
$0
作为指向该闭包开头的指针调用该闭包 内存位置,以及

UnsafePointer<CChar>($0) + idx * lenStrings
UnsafePointer($0)+idx*lenStrings
是指向第idx子阵列开始的指针。上述代码要求
每个子阵列包含一个以NUL结尾的UTF-8字符串

Martin R建议的解决方案对我来说很好,而且从我有限的测试中可以看出,确实有效。然而,正如Martin所指出的,它要求字符串以NUL结尾UTF-8。这里还有两种可能的方法。它们遵循的原则是在C中处理C数据结构的复杂性,而不是在Swift中处理它。您选择哪种方法取决于您在应用程序中对RequestConfiguration的具体操作。如果您不习惯用C语言编程,那么像Martin建议的那种纯粹的快速方法可能是更好的选择

在本讨论中,我们假设第三方C库具有以下检索RequestConfiguration的功能:

const RequestConfiguration * getConfig();
方法1:使RequestConfiguration对象可用于Swift代码,但使用以下C helper函数从中提取名称:

const unsigned char * getNameFromConfig(const RequestConfiguration * rc, unsigned int nameIdx)
{
    return rc->names[nameIdx];
}
Swift代码必须通过桥接头使用此函数的签名和RequestConfiguration类型。然后,您可以在Swift中执行以下操作:

var cfg : UnsafePointer<RequestConfiguration> = getConfig()

if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig(cfg, cfg.memory.currentName)))
{
    print(s)
}
并在Swift中使用,如下所示:

if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig1(2)))
{
    print(s)
}
如果让s=String.fromCString(UnsafePointer(getNameFromConfig1(2)))
{
印刷品
}
这将在位置2打印名称(从0开始计数)。当然,使用这种方法,您可能还希望使用C帮助程序返回名称计数以及当前名称索引

同样,在这两种方法中,假定字符串是NUL终止的UTF-8。还有其他可能的方法,这些只是例子


另外请注意,以上假设您以只读方式访问RequestConfiguration。如果您还希望对其进行修改,并使更改对第三方库C代码可见,则这是一个不同的游戏。

如果C数组是以NUL结尾的UTF-8字符串,则您可以使用此选项:。字符串以NULL结尾,但它们位于可能包含30个字符串的数组中。此解决方案的问题是,在我的问题中我没有解释的是,在我的例子中,我有几个结构中有字符串数组,所以我真的不想为每个结构/字段组合实现一个C函数。谢谢,因为我需要它来实现几个结构/字符串数组组合,通过传入C字符串数组的元组、计数(元素数)和长度(数组中字符串的长度)作为参数,而不是结构,我将其修改为泛型。
var cfg : UnsafePointer<RequestConfiguration> = getConfig()

if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig(cfg, cfg.memory.currentName)))
{
    print(s)
}
const unsigned char * getNameFromConfig1(unsigned int idx)
{
    const RequestConfiguration * p = getConfig();
    return p->names[idx];
}
if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig1(2)))
{
    print(s)
}