#ios #swift #unsafe-pointers
#iOS #swift #небезопасные указатели
Вопрос:
Я считываю данные с внешнего устройства MFi в буфер, используя сторонний SDK «SessionController». Смотрите ниже:
let handle: UInt64 = self.sessionController.openFile(file.path, mode: openMode)
if handle == 0 {
//Error
return
}
let c: UInt64 = file.size
var bytesArray: [UInt8] = [UInt8](fileData)
let bufferPointer: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(c))
bufferPointer.initialize(repeating: 0, count: Int(c))
defer {
bufferPointer.deinitialize(count: Int(c))
bufferPointer.deallocate()
}
var sum: UInt32 = 0
let singleSize: UInt32 = 8 << 20
while sum < c {
let read = self.sessionController.readFile(handle, data: bufferPointer, len: singleSize)
if read == 0 {
//There was an error
return
}
sum = read
}
let newPointer : UnsafeRawPointer = UnsafeRawPointer(bufferPointer)
fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("test.MOV")
fileData = Data(bytes: newPointer, count: Int(c))
try! fileData.write(to: fileURL)
//Now use this fileURL to watch video in an AVPlayer...
//AVPlayer(init: fileURL)
По какой-то причине данные, хранящиеся в fileURL, повреждены (я думаю), и я не могу воспроизвести видеофайл. Я думаю, что я что-то неправильно делаю с небезопасным Swift, но я не уверен, что именно. Как я могу убедиться, что я правильно прочитал данные с устройства в память, а затем извлек эти данные из памяти и сохранил их на жестком диске в fileURL? Что я здесь делаю не так? Видео не будет воспроизводиться в AVPlayer с учетом fileURL.
Ответ №1:
Основная ошибка здесь:
let read = self.sessionController.readFile(handle, data: bufferPointer, len: singleSize)
Если вы читаете несколькими порциями, то второе и все последующие чтения перезапишут данные, прочитанные ранее. Так что, вероятно, это должно быть
let read = self.sessionController.readFile(handle, data: bufferPointer sum, len: singleSize)
Обратите также внимание, что размер файла определяется как UInt64
, но переменной sum
(которая содержит общее количество прочитанных на данный момент байт) является UInt32
. Это приведет к проблемам, если объем данных превышает 4 ГБ.
Но обычно я бы избегал считывать полные данные в буфер памяти. Вы уже читаете фрагментами, поэтому можете сразу записать данные в файл назначения. Вот как это может выглядеть:
// Output file:
let fileURL = ...
let fileHandle = try FileHandle(forWritingTo: fileURL)
defer { fileHandle.closeFile() }
// Buffer:
let bufferSize = 1024 * 1024 // Choose some buffer size
var buffer = Data(count: bufferSize)
// Read/write loop:
let fileSize: UInt64 = file.size
var remainingToRead = fileSize
while remainingToRead > 0 {
let read = buffer.withUnsafeMutableBytes { bufferPointer in
self.sessionController.readFile(handle, data: bufferPointer, len: UInt32(min(remainingToRead, UInt64(bufferSize))))
}
if read == 0 {
return // Read error
}
remainingToRead -= UInt64(read)
fileHandle.write(buffer)
}
Обратите также внимание, что данные считываются непосредственно в Data
значение, вместо того, чтобы считывать их в выделенную память, а затем копировать в другую Data
.