我扩展了swift的数据类型,以便我可以从字符串数组初始化一个实例,然后恢复这些字符串。我有两个版本的初始化程序;其中一个按预期工作,另一个没有。我在这里请求帮助,了解非工作版本的情况。这是扩展名(其中我注释掉一个或另一个初始化程序,然后运行测试代码)
public extension Data {
var encoding: String.Encoding { return .utf8 }
// This version works
public init(with: [String]) {
let data = NSMutableData()
with.forEach {
data.append($0.data(using: String.Encoding.utf8)!)
data.append([0], length: 1)
}
self = data as Data
}
// This version does not work
public init(with: [String]) {
self.init()
with.forEach {
self.append($0.data(using: String.Encoding.utf8)!)
self.append(0)
}
}
public func toStringArray() -> [String] {
var decodedStrings = [String]()
var stringTerminatorPositions = [Int]()
var currentPosition = 0
self.enumerateBytes() {
buffer, count, stop in
print("Enumeration count = \(count)")
for i in 0 ..< count {
if buffer[i] == 0 {
stringTerminatorPositions.append(currentPosition)
}
currentPosition += 1
}
}
var stringStartPosition = 0
for stringTerminatorPosition in stringTerminatorPositions {
let encodedString = self.subdata(in: stringStartPosition ..< stringTerminatorPosition)
if let decodedString = String(data: encodedString, encoding: encoding) {
decodedStrings.append(decodedString)
}
stringStartPosition = stringTerminatorPosition + 1
}
return decodedStrings
}
}
这是测试代码:
let strings = ["one", "two", "three", "four"]
let encoded = Data(with: strings)
let decoded = encoded.toStringArray()
print("\(encoded as NSData) => \(decoded)")
以下是使用工作初始化程序时的输出:
Enumeration count = 19
<6f6e6500 74776f00 74687265 6500666f 757200> => ["one", "two", "three", "four"]
以下是使用非工作初始化程序时的输出:
Enumeration count = 0
<6f6e6500 74776f00 74687265 6500666f 757200> => []
请注意以下事项:
答案 0 :(得分:0)
我猜Swift将构造函数Data(带:strings)桥接到NSData进行实例化,这样就不会让你在self初始化后进行更改。
在你的初始化程序中,你可以对可变实例进行更改,然后将其分配给self,这就是它工作的原因。
即使您在测试中声明编码为var,Swift仍然会在将对象分配给变量之前将其首先构造为不可变实例。
您可以通过将可变实例分配给self而不是使用self.init()来解决此问题:
// This version will now work
public init(with: [String])
{
self = NSMutableData() as Data
with.forEach
{
self.append($0.data(using: String.Encoding.utf8)!)
self.append(0)
}
}
答案 1 :(得分:0)
您的初始化程序没有任何问题 - 问题在于您使用enumerateBytes
:
self.enumerateBytes() {
buffer, count, stop in
print("Enumeration count = \(count)")
for i in 0 ..< count {
if buffer[i] == 0 {
stringTerminatorPositions.append(currentPosition)
}
currentPosition += 1
}
}
你在这里调用count
的是不计数 - 它是给定区域的起始字节的字节索引。事实上,它为您提供了使用后备NSData
存储的字节区域的长度is a bug(将在下一个Swift版本中修复)。
要获取字节区域的计数,您只想说buffer.count
- 或者假设UnsafeBufferPointer
是Sequence
,您还可以说:
var currentPosition = 0
self.enumerateBytes { buffer, byteIndex, stop in
for byte in buffer {
if byte == 0 {
stringTerminatorPositions.append(currentPosition)
}
currentPosition += 1
}
}
或者更简单:
self.enumerateBytes { buffer, byteIndex, stop in
for (offset, byte) in buffer.enumerated() where byte == 0 {
stringTerminatorPositions.append(byteIndex + offset)
}
}
虽然此实现目前不适用于由Data
支持的NSData
个实例,直到错误修复为止,因为byteIndex
会错误地为您提供长度该地区。
但是如果你稍后在实现中调用subdata(in:)
,它会将每个字符串的给定字节复制到一个新的缓冲区中 - 我真的没有看到在这里使用enumerateBytes
的优势。您可以通过使用withUnsafeBytes
来简化您的实现,以便为您提供连续的数据视图:
public func cStringsToArray(encoding: String.Encoding = .utf8) -> [String] {
return withUnsafeBytes { (ptr: UnsafePointer<Int8>) in
var strings = [String]()
var previous = ptr
for offset in 0 ..< count {
let current = ptr + offset
if current != previous && current.pointee == 0 {
// if we cannot decode the string, append a unicode replacement character
// feel free to handle this another way.
strings.append(String(cString: previous, encoding: encoding) ?? "\u{FFFD}")
previous = current + 1
}
}
return strings
}
}