Я унаследовал некоторый код и некоторое время пытался отключить предупреждение для Forming 'UnsafeRawPointer' to a variable of type '[T]'; this is likely incorrect because 'T' may contain an object reference.
. Предупреждение относится к pointer: array
в последней строке. Сам код работает или, кажется, работает, поскольку записанные байты могут быть прочитаны позже без ошибок. Но я хочу сделать это правильно. Кто-нибудь знает, о чем идет речь?
func writeBytes(pointer: UnsafeRawPointer, length: Int, documentName: String) -> Bool {
let data = Data(bytes: pointer, count: length)
do {
try data.write(to: docsDir.appending(path: documentName))
return true
} catch {
print("Error writing bytes for file \(documentName):", error)
return false
}
}
func writeBytesFrom<T>(array: [T], documentName: String) -> Bool {
return writeBytes(pointer: array, length: array.count * MemoryLayout<T>.stride, documentName: documentName)
}
На самом деле я думаю, что предупреждение вполне оправдано. Вы предполагаете, что T
— тривиальный тип. Например, если T
является ссылочным типом, writeBytes
будет записывать ссылки (указатели) на фактические экземпляры (которые не обязательно будут «прочитаны позже без ошибок»), а не свойства каждого экземпляра. Я бы рекомендовал прояснить это в документации или что-то в этом роде.
Помимо проблем, на которые указал Свипер, здесь существует гораздо более фундаментальная проблема. Даже для тривиальных значений (структуры/перечисления, состоящие из других тривиальных структур/перечислений), Swift не гарантирует, что расположение значений в памяти стабильно при запуске приложения, не говоря уже о между компьютерами. Например. может быть разница в целочисленном порядке байтов, 64 бит против 32 бит и т. д. Чтобы сериализовать значения в файл, вам следует использовать более преднамеренный метод сериализации, такой как JSON, YAML, XML, protobuffs и т. д.
Глядя на некоторые обсуждения на forums.swift.org withUnsafePointer(to:)
, кажется, это способ справиться с этим.
func writeBytesFrom<T>(array: [T], documentName: String) -> Bool {
withUnsafePointer(to: array) { pointer in
writeBytes(pointer: pointer, length: array.count * MemoryLayout<T>.stride, documentName: documentName)
}
}
Я думаю, ты прав, спасибо.
Я бы предложил вместо этого использовать array.withUnsafeBtyes { pointer in ... }
(документы ). Он даст вам UnsaferawBufferPointer, свойство count
которого будет правильным количеством байтов (поэтому вам не нужно вручную умножать на шаг). Более того, он не полагается на особое поведение Swift по объединению массивов в указатели, что лично я считаю слишком волшебным и его следует избегать.
@Александр, проблема в том, что Data(bytes...)
нужен UnsafeRawPointer, и использование array.withUnsafeBytes...
дает мне ошибку Cannot convert value of type 'UnsafeRawBufferPointer' to expected argument type 'UnsafeRawPointer'
@johnbakers Вам просто нужно использовать его свойство baseAddress
@Александр, прекрасно, спасибо, сработало
@johnbakers Если вы это пропустили, взгляните на комментарий, который я оставил к вашему основному посту выше.
Так может быть, опубликовать более правильный ответ и отметить его как принятый вместо этого?
Вместо этого я бы предложил использовать Array.withUnsafeBtyes
:
// Danger! This is really fragile!
func writeBytesFrom<T>(array: [T], documentName: String) -> Bool {
array.withUnsafeBtyes { buffer in
writeBytes(pointer: buffer.baseAddress, length: buffer.count, documentName: documentName)
}
}
UnsafeRawBufferPointer, который он вам дает, будет иметь свойство count
с правильным количеством байтов, предварительно рассчитанным для вас (поэтому вам не нужно вручную умножать на шаг).
Более того, он не полагается на особое поведение Swift по объединению массивов в указатели, что лично я считаю слишком волшебным и его следует избегать.
Несмотря на все вышесказанное, это все еще очень хрупкий и граничащий с неправильным кодом.
Помимо проблем, на которые указал Свипер, здесь существует гораздо более фундаментальная проблема.
Даже для тривиальных значений (структуры/перечисления, состоящие из других тривиальных структур/перечислений), Swift не гарантирует, что расположение значений в памяти стабильно при запуске приложения, не говоря уже о между компьютерами. Например. может быть разница в порядке байтов целых чисел: 64 бит против 32 бит и т. д. Чтобы сериализовать значения в файл, вам следует использовать более преднамеренный метод сериализации, такой как JSON, YAML, XML, protobuffs и т. д.
Похожий мой пост.