简体   繁体   English

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

[英]How to cast [Int8] to [UInt8] in Swift

I have a buffer that contains just characters 我有一个只包含字符的缓冲区

let buffer: [Int8] = ....

Then I need to pass this to a function process that takes [UInt8] as an argument. 然后我需要将它传递给一个以[UInt8]为参数的函数process

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

What would be the best way to pass the [Int8] buffer to cast to [Int8]? 将[Int8]缓冲区传递给[Int8]的最佳方法是什么? I know following code would work, but in this case the buffer contains just bunch of characters, and it is unnecessary to use functions like map. 我知道下面的代码可以工作,但在这种情况下,缓冲区只包含一堆字符,并且不必使用map等函数。

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

I am using Xcode7 b3 Swift2. 我正在使用Xcode7 b3 Swift2。

IMO, the best way to do this would be to stick to the same base type throughout the whole application to avoid the whole need to do casts/coercions. IMO,这样做的最好方法是在整个应用程序中坚持使用相同的基本类型,以避免完全需要进行强制转换/强制。 That is, either use Int8 everywhere, or UInt8 , but not both. 也就是说,要么在任何地方使用Int8 ,要么使用UInt8 ,但不能同时使用两者。

If you have no choice, eg if you use two separate frameworks over which you have no control, and one of them uses Int8 while another uses UInt8 , then you should use map if you really want to use Swift. 如果你别无选择,例如你使用两个你无法控制的独立框架,其中一个使用Int8而另一个使用UInt8 ,那么你应该使用map如果你真的想使用Swift。 The latter 2 lines from your examples ( process([UInt8](buffer)) and process(buffer as! [UInt8]) ) look more like C approach to the problem, that is, we don't care that this area in memory is an array on singed integers we will now treat it as if it is unsigneds. 后两行来自你的例子( process([UInt8](buffer))process(buffer as! [UInt8]) )看起来更像C方法的问题,就是我们不在乎这个区域在内存中是一个有关整数的数组,我们现在将它看作是无符号的。 Which basically throws whole Swift idea of strong types to the window. 这基本上把整个Swift的强类型概念抛到了窗口。

What I would probably try to do is to use lazy sequences. 我可能尝试做的是使用延迟序列。 Eg check if it possible to feed process() method with something like: 例如,检查是否可以通过以下方式提供process()方法:

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

process(convertedBuffer)

This would at least save you from extra memory overhead (as otherwise you would have to keep 2 arrays), and possibly save you some performance (thanks to laziness). 这至少可以节省额外的内存开销(否则你必须保留2个数组),并且可能会节省一些性能(由于懒惰)。

I broadly agree with the other answers that you should just stick with map , however, if your array were truly huge, and it really was painful to create a whole second buffer just for converting to the same bit pattern, you could do it like this: 我大致同意你应该坚持使用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]

Note withUnsafeBufferPointer means what it says. 注意withUnsafeBufferPointer意味着它所说的。 It's unsafe and you can corrupt memory if you get this wrong (be especially careful with the count). 这是不安全的,如果你弄错了你可以破坏记忆(尤其要小心计数)。 It works based on your external knowledge that, for example, if any of the integers are negative then your code doesn't mind them becoming corrupt unsigned integers. 它基于您的外部知识工作,例如,如果任何整数是负数,那么您的代码不会介意它们成为损坏的无符号整数。 You might know that, but the Swift type system can't, so it won't allow it without resort to the unsafe types. 你可能知道,但是Swift类型的系统不能,所以它不会允许它而不诉诸不安全的类型。

That said, the above code is correct and within the rules and these techniques are justifiable if you need the performance edge. 也就是说,上述代码是正确的并且在规则范围内, 如果您需要性能优势,这些技术是合理的。 You almost certainly won't unless you're dealing with gigantic amounts of data or writing a library that you will call a gazillion times. 除非你处理大量的数据或编写一个你称之为无数次的库,否则你几乎肯定不会。

It's also worth noting that there are circumstances where an array is not actually backed by a contiguous buffer (for example if it were cast from an NSArray ) in which case calling .withUnsafeBufferPointer will first copy all the elements into a contiguous array. 值得注意的是,在某些情况下,数组实际上并未由连续缓冲区支持(例如,如果它是从NSArray .withUnsafeBufferPointer ),在这种情况下,调用.withUnsafeBufferPointer将首先将所有元素复制到一个连续的数组中。 Also, Swift arrays are growable so this copy of underlying elements happens often as the array grows. 此外,Swift数组是可扩展的,因此底层元素的副本通常随着数组的增长而发生。 If performance is absolutely critical, you could consider allocating your own memory using UnsafeMutablePointer and using it fixed-size style using UnsafeBufferPointer . 如果性能是绝对关键的,你可以考虑使用分配自己的记忆UnsafeMutablePointer ,并用它使用固定大小的风格UnsafeBufferPointer

For a humorous but definitely not within the rules example that you shouldn't actually use, this will also work: 对于一个幽默但绝对不在你不应该实际使用的规则示例中,这也将起作用:

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

It's also worth noting that these solutions are not the same as a.map { UInt8($0) } since the latter will trap at runtime if you pass it a negative integer. 值得注意的是,这些解决方案与a.map { UInt8($0) }因为如果你传递一个负整数,后者将在运行时陷阱。 If this is a possibility you may need to filter them first. 如果有可能,您可能需要先过滤它们。

You cannot cast arrays in Swift. 你不能在Swift中转换数组。 It looks like you can, but what's really happening is that you are casting all the elements, one by one. 看起来像你可以,但真正发生的是你逐一投射所有元素。 Therefore, you can use cast notation with an array only if the elements can be cast. 因此,只有在可以强制转换元素时, 才能对数组使用强制转换表示法。

Well, you cannot cast between numeric types in Swift. 好吧,你不能在Swift中的数值类型之间强制转换。 You have to coerce, which is a completely different thing - that is, you must make a new object of a different numeric type, based on the original object. 你必须强迫,这是一个完全不同的东西 - 也就是说,你必须根据原始对象创建一个不同数字类型的新对象。 The only way to use an Int8 where a UInt8 is expected is to coerce it: UInt8(x) . 使用期望UInt8的Int8的唯一方法是强制它: UInt8(x)

So what is true for one Int8 is true for an entire array of Int8. 因此,对于整个Int8 数组一个 Int8的情况是正确的。 You cannot cast from an array of Int8 to an array of UInt8, any more than you could cast one of them. 你不能从一个Int8数组转换为一个UInt8数组,而不是你可以构建其中一个 The only way to end up with an array of UInt8 is to coerce all the elements. 结束UInt8数组的唯一方法是强制所有元素。 That is exactly what your map call does. 这正是你的map调用所做的。 That is the way to do it; 这是做到这一点的方法; saying it is "unnecessary" is meaningless. 说这是“不必要的”是没有意义的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM