iOS中FileHandle.readData(ofLength:)内存未释放问题求助
大文件分片上传内存过高崩溃问题
在实现大文件分片上传时,调用FileHandle.readData(ofLength:)后内存持续攀升,分片内存无法释放,最终触发OOM异常崩溃。Profiler分析显示问题根源在该读取方法上,相关代码如下:
func nextChunk(then: @escaping (Data?) -> Void) { self.previousOffset = self.fileHandle.offsetInFile autoreleasepool { let data = self.fileHandle.readData(ofLength: Constants.chunkLength) if data == Constants.endOfFile { then(nil) } else { then(data) self.currentChunk += 1 } } }
Profiler截图显示:
- 内存占用随分片读取持续上升,
FileHandle.readData(ofLength:)方法占据大量内存资源 - 内存峰值逼近系统限制,触发OOM预警
问题分析与解决办法
核心原因
当前代码中,autoreleasepool仅包裹了读取逻辑,但读取生成的Data被传递到闭包then中。如果上传操作未完成就继续读取下一个分片,多个Data对象会同时驻留在内存中导致堆积;另外,闭包可能隐式持有self,间接延长FileHandle和Data的生命周期。
修复方案
控制分片读取节奏
不要连续读取多个分片,改为等待当前分片上传完成后再调用nextChunk(),确保同一时间内存中只存在一个分片的Data。扩大autoreleasepool作用域
将闭包执行逻辑纳入autoreleasepool,确保Data在处理完成后及时释放:func nextChunk(then: @escaping (Data?) -> Void) { self.previousOffset = self.fileHandle.offsetInFile autoreleasepool { let data = self.fileHandle.readData(ofLength: Constants.chunkLength) let result = data == Constants.endOfFile ? nil : data then(result) if result != nil { self.currentChunk += 1 } } }若
then闭包为异步执行,需在闭包内部处理完数据后手动置空Data引用,加速内存释放。改用低内存读取方式
对于超大文件,推荐使用FileHandle.read(into:)直接写入预先分配的缓冲区,减少Data对象的内存开销:func nextChunk(then: @escaping (Data?) -> Void) { self.previousOffset = self.fileHandle.offsetInFile autoreleasepool { var buffer = [UInt8](repeating: 0, count: Constants.chunkLength) let bytesRead = buffer.withUnsafeMutableBytes { self.fileHandle.read(into: $0) } if bytesRead == 0 { then(nil) } else { let chunkData = Data(buffer.prefix(bytesRead)) then(chunkData) self.currentChunk += 1 } } }排查闭包强引用
检查then闭包是否存在对self的强引用,必要时使用[weak self]避免循环引用,确保FileHandle能在不需要时被释放。
额外优化建议
- 使用Profiler的Leaks工具检测是否存在其他内存泄漏点
- 限制并发上传的分片数量(比如最多2-3个),避免内存占用过载
- 读取完成后及时确认
FileHandle的偏移位置,防止重复读取同一区域
内容的提问来源于stack exchange,提问作者Pavel Sinkevich
相关产品推荐
相关产品推荐

