使用 NSFileHandle 分塊讀取 25-30MB 大文件並寫入 ZIP 流時,應用因內存佔用過高被系統強殺(Jetsam)。代碼在循環中持續創建 NSData 對象,且未及時釋放,導致內存峰值過高。
NSData *buffer = [fileHandle readDataOfLength:chunkSize] 每次創建新對象。@autoreleasepool,臨時對象累積在主釋放池中,直到循環結束才釋放。NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:sourcePath];
NSOutputStream *streamBIG = [NSOutputStream outputStreamToMemory]; // 或寫入文件流
[streamBIG open];
const NSUInteger chunkSize = 64 * 1024; // 64KB 分塊
uint64_t offset = 0;
uint64_t fileSize = [self fileSizeAtPath:sourcePath];
NSData *buffer = [fileHandle readDataOfLength:chunkSize];
while ([buffer length] > 0) {
@autoreleasepool {
// 關鍵:每次循環創建獨立釋放池,及時釋放 buffer 等臨時對象
[streamBIG writeData:buffer];
offset += [buffer length];
[fileHandle seekToFileOffset:offset];
buffer = [fileHandle readDataOfLength:chunkSize];
}
}
[streamBIG close];
[fileHandle closeFile];
// 根據文件大小動態調整,平衡內存與 I/O 次數
NSUInteger chunkSize = fileSize > 100*1024*1024 ? 1024*1024 : 64*1024; // 大文件 1MB,小文件 64KB
dispatch_io_t readChannel = dispatch_io_create_with_path(DISPATCH_IO_STREAM,
sourcePath, O_RDONLY, 0, queue);
dispatch_io_t writeChannel = dispatch_io_create_with_path(DISPATCH_IO_STREAM,
destPath, O_WRONLY | O_CREAT | O_TRUNC, 0644, queue);
dispatch_io_read(readChannel, 0, SIZE_MAX, queue, ^(bool done, dispatch_data_t data, int error) {
if (data) {
dispatch_io_write(writeChannel, 0, data, queue, ^(bool done, dispatch_data_t data, int error) {
// 寫入完成回調
});
}
if (done) {
dispatch_io_close(readChannel, 0);
dispatch_io_close(writeChannel, 0);
}
});
優勢:系統自動管理內存映射、分頁讀寫、內存壓力釋放,適合超大文件(>100MB)。
成熟開源庫已處理內存管理、多線程、進度回調、加密等複雜邏輯,推薦生產環境直接使用。
MallocStackLogging=1 獲取分配堆棧。