如何在Swift中获取任意数组的字节大小?

6
我想使用let rawDataFromArray = NSData(bytes: myArray, length: ???),但不知道如何获取我的数组的字节数。以下是一些可能的数组示例:
let arr1 = [1, 2, 3]
let arr2 = [1.0, 23556789000.0]
let arr3 = ["hello", "ok", ""]

func arrayLength(myArray: Array) -> Int {
    var bytes = 0
    for object in myArray {
        // not sure what to do here
    }
    return bytes
}

我不确定遍历数组中的每个元素(字符串的情况下,遍历每个字符,因为表情符可能会有更多字节来表示它们)是否是正确的方法。

如何获取数组的字节大小?
有没有人能告诉我正确的做法?
或者在Swift中将Array转换为NSData并不是一个好习惯?

我也看到了将Swift数组转换为NSData进行持久化存储将字节数组转换为NSData自定义数组转换为NSData,但是无法弄清楚如何获取任意数组的字节大小。


2
请参考https://dev59.com/PF8e5IYBdhLWcg3wd6Q6,这是一个可以用于整数或浮点数数组的示例。但是你不能简单地将*字符串*数组视为NSData,因为`String`结构(具有固定大小)包含指向实际字符存储的不透明指针。 - Martin R
谢谢@MartinR。看起来将字符串数组转换为NSData是一个不好的想法。您能否更详细地解释一下什么是不透明指针? - Andrej
1
struct String 有一些成员不属于“可见”的API(但你可以在调试器中看到它们)。其中一些是指向字符串实际存储使用的指针。(因此,例如,相同的字符串可以共享存储空间。)——重点是struct String不是自包含的。如果你将其打包到NSData中,然后将其传输到其他地方并解包它,它将包含无效的指针。当然,你可以从字符串数组创建NSData,但必须分别附加每个字符串(例如作为NUL终止的UTF-8字符串)。 - Martin R
或者,使用NSKeyedArchiver(就像您引用的线程中一样)。这取决于您想要对数据做什么。 - Martin R
1个回答

6

似乎有一些误解:对于每种类型T,所有T的实例都具有相同的大小,可以通过sizeof(T)进行计算。在数组的情况下,数组元素之间可能存在填充,因此arr1所需的总大小为

arr1.count * strideof(Int)

因此,创建从数组中创建NSData的通用函数如下:(例如,请参见Swift:如何使用sizeof?以了解sizeof()strideof()之间的微妙差异。)
extension Array {
    func asData() -> NSData {
        return self.withUnsafeBufferPointer({
            NSData(bytes: $0.baseAddress, length: count * strideof(Element))
        })
    }
}

使用withUnsafeBufferPointer()可以确保数组使用连续的存储空间来存储元素。
对于像IntFloat这样的"简单"类型,这会产生预期的结果:
let arr1 = [1, 2, 3]
print(arr1.asData())
// <01000000 00000000 02000000 00000000 03000000 00000000>

let arr2 = [1.0, 23556789000.0]
print(arr2.asData())
// <00000000 0000f03f 0000204c 60f01542>

然而,对于一个字符串数组无效:

let arr3 = ["hello", "ok", ""]
print(arr3.asData())
// <945b2900 01000000 05000000 00000000 00000000 00000000 9a5b2900 01000000 02000000 00000000 00000000 00000000 068d2900 01000000 02000000 00000080 00000000 00000000>

因为struct String包含指向实际字符存储的(隐藏/未记录)指针。

一种可能的方法是将每个字符串作为以NUL结尾的UTF-8字符串追加:

let data3 = NSMutableData()
arr3.forEach { string in
    string.withCString {
        data3.appendBytes($0, length: Int(strlen($0)) + 1)
    }
}
print(data3)
// <68656c6c 6f006f6b 00f09f91 8d00>

或者,像你提到的那些线程一样使用NSKeyedArchiver


1
strideof(T)MemoryLayout<T>.stride(自 Swift 3 开始?) - Bobjt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接