如何在Swift中将[Int8]转换为[UInt8]

时间:2015-07-11 14:39:08

标签: swift

我有一个只包含字符的缓冲区

let buffer: [Int8] = ....

然后我需要将它传递给以[UInt8]作为参数的函数process

func process(buffer: [UInt8]) {
    // some code
}

将[Int8]缓冲区传递给[Int8]的最佳方法是什么?我知道下面的代码可以工作,但在这种情况下,缓冲区只包含一堆字符,并且不必使用map等函数。

process(buffer.map{ x in UInt8(x) }) // OK
process([UInt8](buffer)) // error
process(buffer as! [UInt8]) // error

我正在使用Xcode7 b3 Swift2。

3 个答案:

答案 0 :(得分:5)

IMO,最好的方法是在整个应用程序中坚持使用相同的基本类型,以避免完全需要进行强制转换/强制转换。也就是说,要么在任何地方都使用Int8,要么UInt8,而不是两者都使用。

如果你别无选择,例如如果你使用两个你无法控制的独立框架,其中一个框架使用Int8而另一个框架使用UInt8,那么如果你真的想使用Swift,你应该使用map。后两行来自您的示例(process([UInt8](buffer))和  process(buffer as! [UInt8]))看起来更像C方法来处理问题,也就是说,我们并不关心内存中的这个区域是一个有关整数的数组,我们现在将它看作是无符号的。这基本上抛出了整个Swift对强类型的看法。

我可能尝试做的是使用延迟序列。例如。检查是否可以使用以下内容提供process()方法:

let convertedBuffer = lazy(buffer).map() {
    UInt8($0)
}

process(convertedBuffer)

这至少可以节省额外的内存开销(否则你必须保留2个数组),并且可能会为你节省一些性能(感谢懒惰)。

答案 1 :(得分:4)

我大致同意你应该坚持map的其他答案,但是,如果你的数组真的很大,那么创建一个完整的第二个缓冲区只是为了转换为相同的位模式真的很痛苦你可以这样做:

// first, change your process logic to be generic on any kind of container
func process<C: CollectionType where C.Generator.Element == UInt8>(chars: C) {
    // just to prove it's working...
    print(String(chars.map { UnicodeScalar($0) }))
}

// sample input
let a: [Int8] = [104, 101, 108, 108, 111]  // ascii "Hello"

// access the underlying raw buffer as a pointer
a.withUnsafeBufferPointer { buf -> Void in
    process(
        UnsafeBufferPointer(
            // cast the underlying pointer to the type you want
            start: UnsafePointer(buf.baseAddress), 
            count: buf.count))
}
// this prints [h, e, l, l, o]

注意withUnsafeBufferPointer表示它的含义。这是不安全的,如果你弄错了你可以破坏记忆(尤其要小心计数)。它基于您的外部知识工作,例如,如果任何整数是负数,那么您的代码不会介意它们变成腐败的无符号整数。您可能知道这一点,但Swift类型的系统无法实现,因此它不会在不使用不安全类型的情况下允许它。

也就是说,上面的代码是正确的并且在规则范围内,这些技术是合理的如果你需要性能优势。你几乎肯定不会,除非你处理大量的数据或编写一个你会称之为无数次的数据库。

还值得注意的是,在某些情况下,数组实际上并未由连续缓冲区支持(例如,如果它是从NSArray强制转换的),在这种情况下,调用.withUnsafeBufferPointer将首先复制所有将元素转换为连续数组。此外,Swift数组是可扩展的,因此底层元素的副本通常随着数组的增长而发生。如果性能绝对至关重要,您可以考虑使用UnsafeMutablePointer分配自己的内存并使用UnsafeBufferPointer使用固定大小的样式。

对于一个幽默但绝对的规则示例中你不应该实际使用,这也会有效:

process(unsafeBitCast(a, [UInt8].self))

同样值得注意的是,这些解决方案与a.map { UInt8($0) }不同,因为如果你传递一个负整数,后者将在运行时陷阱。如果有可能,您可能需要先过滤它们。

答案 2 :(得分:2)

你无法在Swift中转换数组。 看起来像你一样,但真正发生的是你逐一投射所有元素。因此,只有在可以转换元素时,才能对数组使用强制转换符号。

好吧,你无法在Swift中的数值类型之间进行转换。你必须强迫,这是一个完全不同的东西 - 也就是说,你必须根据原始对象创建一个不同数字类型的新对象。使用期望UInt8的Int8的唯一方法是强制它:UInt8(x)

对于一个,对于Int8的整个数组,Int8是正确的。你不能从一个Int8数组转换为一个UInt8数组,只不过你可以强制转换它们的一个。结束UInt8数组的唯一方法是强制所有元素。这正是您的map电话的作用。这是做到这一点的方法;说这是“不必要的”是没有意义的。

相关问题