使用Swift从NSData中获取数据 [英] Getting data out of NSData with Swift

查看:135
本文介绍了使用Swift从NSData中获取数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现SwiftNSData是沮丧的邪恶婚姻.我发现每次处理此事时,我都会感觉像所有假定的新发现的Swift安全性都消失了.崩溃数量(带有无用的痕迹)无济于事.

I am finding Swift and NSData to be an unholy matrimony of frustration. I find I feel like all the supposed new found Swift safety goes out the window every time I deal with this thing. The amount of crashes (with unhelpful traces) don't help.

因此,我了解到,我可以通过执行以下操作来避免出现UnsafeMutablePointer令人恐惧的事情:

So, I've learned, that I can avoid the scary UnsafeMutablePointer stuff by doing things like the following:

var bytes = [UInt8](count: 15, repeatedValue: 0)
anNSData.getBytes(&bytes, length=15)

我还发现,我可以直接提取为奇异值:

I also discovered, that I can extract directly into singular values:

var u32:UInt32 = 0
anNSData.getBytes(&u32, length=4)

这导致了两个中间问题:

This leads to two intermediate questions:

1)有什么我可以使用的东西比那里的硬编码常量更可靠.如果这是C,我将只使用sizeof.但是我想我读到也许应该使用strideof而不是sizeof吗?那在[UInt8]上不起作用,对吗?

1) Is there something I can use that is more reliable than hardcoded constants there. If this were C, I'd just use sizeof. But I think I read that maybe I should be using strideof instead sizeof? And that wouldn't work on [UInt8]'s, would it?

2)文档(对于Swift)说该参数应该为_ buffer: UnsafeMutablePointer<Void>.那么这是如何工作的呢?我只是幸运吗?为什么我要代替本地/托管的[Uint8]构造呢?我想知道UnsafeMutablePointer是不是一个协议,但是它是一个结构.

2) The docs (for Swift) says that this parameter is supposed to be _ buffer: UnsafeMutablePointer<Void>. So how does this work? Am I just getting lucky? Why would I want to do that instead of the more native/managed [Uint8] construct?? I wondered if UnsafeMutablePointer was a protocol, but it's a struct.

对于直接读取值(而不是作为数组)感到困惑,我想也许我可以尝试另一种结构.我有一个6字节的结构,如下所示:

Emboldened with reading the values directly (rather than as an Array), I thought maybe I could try another kind of struct. I have a 6 byte struct that looks like:

struct TreeDescription : Hashable {
    var id:UInt32 = 0x00000000
    var channel:UInt8 = 0x00
    var rssi:UInt8 = 0x00

    var hashValue:Int {
        return Int(self.id)
    }
}

实际上是可行的(在认为没有用之后,但最终进行了清理,使一些崩溃消失了!)

Which actually works (after thinking it didn't, but eventually doing a clean which made some crashes go away)!

var tree = TreeDescription()
anNSData.getBytes(&newTree, length: 6)

但这使我担心结构包装的细节吗?为什么这样做?我应该为此担心什么?

But this leads me to worries about structure packing details? Why does this work? What should I be worrying about doing this?

这一切对我来说都是C-ish.我以为Swift将C语言从ObjectiveC中移除了.

This all feels very C-ish to me. I thought Swift took the C out of ObjectiveC.

推荐答案

您可能想签出 RawData 这真的很新,这个人只是对这个想法进行了一些试验,所以不要认为它已经通过了很好的测试,甚至还没有实现任何功能,甚至还没有实现某些功能.基本上,它是一个Swift-y包装器(围绕它,您猜对了)原始数据,一系列字节.

You may want to check out RawData which is really new and this guy just experimented a bit with this idea, so don't think that it's tested well or anything, some function aren't even implemented yet. It's basically a Swift-y wrapper around (you guessed it) raw data, a series of bytes.

使用此扩展名,可以使用NSData实例对其进行初始化:

Using this extension, you can initialise it with an NSData instance:

extension RawData {
    convenience init(data: NSData) {
        self.init(UnsafeMutableBufferPointer(start: UnsafeMutablePointer(data.bytes), count: data.length))
    }
}

您应该这样称呼它:

let data = "Hello, data!".dataUsingEncoding(NSASCIIStringEncoding)!

let rawData = RawData(data: data)

要回答您的问题:

问题在于数据可能非常大.通常您不希望复制大型内容,因为空间很宝贵. [UInt8]值数组与NSData实例之间的区别在于,每次都会复制该数组,将其提供给函数->新副本,然后进行赋值->新副本.对于大数据来说,这不是很理想.

The thing is that data can be large, very large. You generally don't want to copy large stuff, as space is valuable. The difference between an array of [UInt8] values and an NSData instance is that the array gets copied every time, you give it to a function -> new copy, you do an assignment -> new copy. That's not very desirable with large data.

1)如果您想要最原生,最安全的方法,而没有提到的任何第三方库,则可以执行以下操作:

1) If you want the most native, safe way, without any 3rd party libraries as the one mentioned, you can do this:

let data = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(data.bytes), count: data.length)

(我知道这听起来不太安全,但请相信我确实如此).您可以像使用普通数组一样使用它:

(I know it doesn't sound very safe, but believe me it is). You can use this almost like an ordinary array:

let data = "Hello, data!".dataUsingEncoding(NSASCIIStringEncoding)!

let bytes = UnsafeMutableBufferPointer(start: UnsafeMutablePointer<UInt8>(data.bytes), count: data.length)

for byte in bytes {}
bytes.indexOf(0)
bytes.maxElement()

,并且在传递数据时不会复制数据.

and it doesn't copy the data as you pass it along.

2)UnsafeMutablePointer<Void>确实非常类似于C,在这种情况下,它表示指针序列中的起始值(也称为基数). Void类型也来自C,这意味着指针不知道它存储的是哪种值.您可以像这样将各种类型的指针强制转换为所需的类型:UnsafeMutablePointer<Int>(yourVoidPointer)(这不会崩溃).如前所述,您可以使用UnsafeMutableBufferPointer将该类型用作您的类型的集合. UnsafeMutableBufferPointer只是基本指针和长度的包装(这解释了我使用的初始化程序).

2) UnsafeMutablePointer<Void> is indeed very C-like, in this context it represents the starting value (also called base) in a sequence of pointers. The Void type comes from C as well, it means that the pointer doesn't know what kind of value it's storing. You can cast all kinds of pointers to the type you're expecting like this: UnsafeMutablePointer<Int>(yourVoidPointer) (This shouldn't crash). As mentioned before, you can use UnsafeMutableBufferPointer to use it as a collection of your type. UnsafeMutableBufferPointer is just a wrapper around your base pointer and the length (this explains the initialiser I used).

您将数据直接解码到struct中的方法确实有效,即使在编译后,struct的属性也处于正确的顺序,并且struct的大小恰好是其存储属性的总和.对于像您这样的简单数据,那完全没问题.还有一种选择:使用NSCoding协议.优点:更安全.缺点:您必须继承NSObject.我认为您应该坚持现在的方式.不过,我要更改的一件事是将对结构的解码放入结构本身中,并使用sizeof.像这样:

Your method of decoding the data directly into your struct does indeed work, the properties of a struct are in the right order, even after compile time, and the size of a struct is exactly the sum of it's stored properties. For simple data like yours that's totally fine. There is an alternative: To use the NSCoding protocol. Advantage: safer. Disadvantage: You have to subclass NSObject. I think you should be sticking to the way you do it now. One thing I would change though is to put the decoding of your struct inside the struct itself and use sizeof. Have it like this:

struct TreeDescription {
    var id:UInt32 = 0x00000000
    var channel:UInt8 = 0x00
    var rssi:UInt8 = 0x00

    init(data: NSData) {
        data.getBytes(&self, length: sizeof(TreeDescription))
    }
}

另一个您始终可以使用返回类型为T的方法memoryUnsafe(Mutable)Pointer<T>获取基础数据.如果需要,您可以始终通过向其添加/减去Int来移动指针(例如,获取下一个值).

Another You can always get the underlying data from an Unsafe(Mutable)Pointer<T> with the method memory whose return type is T. If you need to you can always shift pointers (to get the next value e.g.) by just adding/subtracting Ints to it.

编辑以回答您的评论:您可以使用&传递inout变量,然后可以在函数中对其进行修改.因为inout变量与传递指针基本相同,所以Swift开发人员决定可以为期望UnsafeMutablePointer的参数传递&value.演示:

EDIT answering your comment: You use the & to pass an inout variable, which can then be modified within the function. Because an inout variable is basically the same as passing the pointer, the Swift devs decided to make it possible to pass &value for an argument that is expecting an UnsafeMutablePointer. Demonstration:

func inoutArray(inout array: [Int]) {}

func pointerArray(array: UnsafeMutablePointer<Int>) {}

var array = [1, 2, 3]

inoutArray(&array)
pointerArray(&array)

这也适用于structs(也许还有其他一些东西)

This also works for structs (and maybe some other things)

这篇关于使用Swift从NSData中获取数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆