Я не рекомендую способ мутировать Data
, используя это:
data.replaceSubrange(0..<2, with: UnsafeBufferPointer(start: &self.crc, count: 1))
Пожалуйста, попробуйте это:
data.replaceSubrange(0..<2, with: &self.crc, count: 2)
Трудно объяснить, почему, ноЯ попробую ...
В Swift параметры inout
работают в семантике копирование-в-копирование.Когда вы пишете что-то вроде этого:
aMethod(¶m)
Swift выделяет некоторую область достаточного размера для содержания param
,
копирует param
в регион, (copy-in)
вызывает метод с передачей адреса региона,
и когдаПри возврате из вызова копирует содержимое региона обратно в param
(копирование).
Во многих случаях Swift оптимизирует (что может произойти даже в настройке -Onone
) шаги, просто передавая фактический адрес param
, но он явно не задокументирован.
Итак, когда параметр inout
передается инициализатору UnsafeBufferPointer
, адрес, полученный UnsafeBufferPointer
может указывать на временную область, которая будет освобождена сразу после завершения инициализации.
Таким образом, replaceSubrange(_:with:)
может скопировать байты в уже освобожденной области в Data
.
Я полагаю, что первый код будет работать в этом случае, так как crc
является свойством структурыt, но если есть простая и безопасная альтернатива, вам лучше избегать небезопасного пути.
ДОПОЛНИТЕЛЬНО для комментария к собственному ответу Брэндона Манцзы.
data.append(UnsafeBufferPointer(start: &self.crcOfRecordData, count: 1))
Использование сейф в значении выше.Это не безопасно , по той же причине, описанной выше.
Я бы написал это как:
data.append(Data(bytes: &self.crcOfRecordData, count: MemoryLayout<UInt16>.size))
(Предполагая тип crcOfRecordData
как UInt16
.)
Если вы не предпочитаете создавать дополнительный Data
экземпляр, вы можете написать его как:
withUnsafeBytes(of: &self.crcOfRecordData) {urbp in
data.append(urbp.baseAddress!.assumingMemoryBound(to: UInt8.self), count: MemoryLayout<UInt16>.size)
}
Это не упоминается в комментарии, но взначение выше safe , следующая строка не является safe .
let uint32Data = Data(buffer: UnsafeBufferPointer(start: &self.someData, count: 1))
Все по той же причине.
Я бы написал это как:
let uint32Data = Data(bytes: &self.someData, count: MemoryLayout<UInt32>.size)
Хотя наблюдаемое неожиданное поведение может происходить в очень ограниченном состоянии и с очень малой вероятностью.
Такое поведение может иметь место только при соблюдении следующих двух условий:
Компилятор Swift генерирует неоптимизированный код "копировать-в-копировать"
Между очень узким периодом, поскольку освобождается временная областьпока метод append
(или Data.init
) не закончит копирование всего содержимого, регион будет изменен для другого использования.
Условие № 1 становится истинным только в ограниченных случаях в текущей реализации Swift.
Условие № 2 возникает очень редко только в многопоточной среде.(Тем не менее, платформа Apple использует много скрытых потоков, как вы можете найти в отладчике XCode.)
На самом деле, я не видел никаких вопросов, касающихся небезопасных случаев выше, мой safe может быть чем-то вроде overkill .
Но альтернативные safe коды не так сложны, не так ли?На мой взгляд, вам лучше привыкнуть использовать код, безопасный для всех случаев .