Data <-> MutableRandomAccessSlice
Я действительно борюсь с тем, что someData[start...stop]
возвращает MutableRandomAccessSlice
, мой someData
был let
для начала, так зачем мне мутабельная вещь? Почему бы мне не получить только RandomAccessSlice
, Что действительно расстраивает, так это то, что он возвращает то, что API довольно несовместимо с исходным кодом. С Data
, Я могу использовать .withUnsafeBytes
но не так с этим потомством. И то, как вы превращаете Срез обратно в Данные, тоже не ясно. Там нет инициала, который принимает один из них.
Я мог бы использовать subdata(in:)
метод вместо подписки, но тогда какой смысл в индексе, если я только когда-либо захочу, чтобы представление подколлекции имело вид оригинальной коллекции. Кроме того, subdata
Метод может делать только открытые поддиапазоны, поэтому индекс может делать как закрытые, так и открытые. Это что-то, что они еще не дошли до финала Swift3?
2 ответа
Помните, что MutableRandomAccessSlice
Вы получаете обратно тип значения, а не ссылочный тип. Это просто означает, что вы можете изменить его, если хотите, но это не имеет ничего общего с тем, из чего вы его вырезали:
let x = Data(bytes: [1,2,3]) // <010203>
var y = x[0...1]
y[0] = 2
x // <010203>
Если вы посмотрите в коде, вы заметите, что намерением является возвращение пользовательского типа среза:
public subscript(bounds: Range<Index>) -> MutableRandomAccessSlice<Data> {
get {
return MutableRandomAccessSlice(base: self, bounds: bounds)
}
set {
// Ideally this would be:
// replaceBytes(in: bounds, with: newValue._base)
// but we do not have access to _base due to 'internal' protection
// TODO: Use a custom Slice type so we have access to the underlying data
let arrayOfBytes = newValue.map { $0 }
arrayOfBytes.withUnsafeBufferPointer {
let otherData = Data(buffer: $0)
replaceBytes(in: bounds, with: otherData)
}
}
}
Тем не менее, пользовательский фрагмент все равно не будет приемлем для функции, которая принимает данные. Это согласуется с другими типами, такими как Array, который срезает ArraySlice, который нельзя передать там, где ожидается Array. Это сделано по замыслу (и, вероятно, для данных также по тем же причинам). Проблема заключается в том, что срез "закрепляет" всю память, которая его поддерживает. Так что, если вы взяли 3-байтовый фрагмент из мегабайта данных и сохранили его в иваре, весь мегабайт должен находиться рядом. Теория (в соответствии с разработчиками Swift, с которыми я говорил) состоит в том, что массивы могут быть массивными, поэтому вы должны быть осторожны с их нарезкой, в то время как строки обычно намного меньше, так что строка может быть разрезана до строки.
По моему опыту, вы, как правило, хотите subdata(in:)
, Мои эксперименты с ним заключаются в том, что по скорости он очень похож на нарезку, поэтому я считаю, что он все еще копируется при записи (но, похоже, он не закрепляет память и в моих первоначальных тестах). Пока что я тестировал только на Mac. Вполне возможно, что на устройствах iOS есть более существенные различия в производительности.
Основываясь на комментариях Роба, я просто добавил следующее расширение Python:
extension Data {
subscript(start:Int?, stop:Int?) -> Data {
var front = 0
if let start = start {
front = start < 0 ? Swift.max(self.count + start, 0) : Swift.min(start, self.count)
}
var back = self.count
if let stop = stop {
back = stop < 0 ? Swift.max(self.count + stop, 0) : Swift.min(stop, self.count)
}
if front >= back {
return Data()
}
let range = Range(front..<back)
return self.subdata(in: range)
}
}
Таким образом, я могу просто сделать
let input = Data(bytes: [0x60, 0x0D, 0xF0, 0x0D])
input[nil, nil] // <600df00d>
input[1, 3] // <0df0>
input[-2, nil] // <f00d>
input[nil, -2] // <600d>