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

Kaz*_*awa 4 swift

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

let buffer: [Int8] = ....
Run Code Online (Sandbox Code Playgroud)

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

func process(buffer: [UInt8]) {
    // some code
}
Run Code Online (Sandbox Code Playgroud)

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

process(buffer.map{ x in UInt8(x) }) // OK
process([UInt8](buffer)) // error
process(buffer as! [UInt8]) // error
Run Code Online (Sandbox Code Playgroud)

我正在使用Xcode7 b3 Swift2.

cou*_*elk 5

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

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

我可能尝试做的是使用延迟序列.例如,检查是否可以通过以下方式提供process()方法:

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

process(convertedBuffer)
Run Code Online (Sandbox Code Playgroud)

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


Air*_*ity 5

我大致同意你应该坚持的其他答案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]
Run Code Online (Sandbox Code Playgroud)

注意withUnsafeBufferPointer就是它所说的意思。这是不安全的,如果你弄错了,你可能会破坏内存(特别注意计数)。它基于您的外部知识工作,例如,如果任何整数为负,那么您的代码不介意它们成为损坏的无符号整数。你可能知道这一点,但 Swift 类型系统不能,所以它不会在不求助于不安全类型的情况下允许它。

也就是说,上面的代码是正确的并且符合规则,如果您需要性能优势,这些技术是合理的。除非您正在处理大量数据或编写一个您将调用无数次的库,否则您几乎肯定不会这样做。

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

对于一个幽默但绝对属于您实际上不应该使用的规则示例,这也适用:

process(unsafeBitCast(a, [UInt8].self))
Run Code Online (Sandbox Code Playgroud)

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