我试图理解Core Data属性的Allows External Storage
属性的行为,看看它是否可以避免我手动在文件系统中存储文件。我想看看它在处理非常大的文件时会如何执行。为此,我创建了一个虚拟项目,并使用Core Data存储了一个大文件(2GB)。然后,我在获取和处理数据时监控了内存使用情况,令我惊讶的是,它没有超过48 MB!为什么会这样?它是否以块形式获取数据?如果是,如何做到?Data
结构体是否有允许Core Data这样做的API?
更多关于我所做的事情的细节:
1.创建了一个只有两个属性的实体File
,fileName
(字符串)和data
(数据)。
1.已检查Allows External Storage
属性的data
属性。
1.已在File
实体中存储2 GB文件。我把这段代码放在viewDidLoad
方法中来做这件事。
do {
// Store file
let fileURL = Bundle.main.url(forResource: "RawData/LargeFile", withExtension: nil)!
let file = File(context: AppDelegate.viewContext)
file.name = fileName
file.data = try Data(contentsOf: fileURL)
try AppDelegate.viewContext.save()
} catch {
print(error.localizedDescription)
}
- 关闭了应用程序,并在
viewDidLoad
中使用新代码重新启动它,以获取和处理大文件的数据。
let fileData = File.files(named: name).first!.data!
DispatchQueue.global(qos: .userInteractive).async {
let result = self.process(data: fileData)
print("The result: \(result)")
}
- File类的
files
静态方法返回File实体中的所有文件。
下面是process方法,它循环遍历数据,逐字节读取并执行异或运算,然后返回结果。它可以是任何方法,这里重要的是读取数据的所有字节。
private func process(data: Data) -> UInt8 {
print("Processsing \(data.count) bytes")
var accumulator: UInt8 = 0
for byte in data {
accumulator ^= byte
}
return accumulator
}
- 我监控了内存使用情况。
我很确定它与核心数据有关,而不是Data
,因为从磁盘加载data
时,执行相同的步骤将导致3 + GB内存使用(还有,为什么额外的1 GB?).
最后,有没有理由不使用Allows External Storage
功能,而是在文件系统中手动存储文件?我知道这个问题已经讨论了很多;但是我读到的大多数建议使用手动方式的观点都提到了Core Data的性能问题,尽管我的小实验表明Core Data性能很好。
任何帮助将不胜感激!
2条答案
按热度按时间bqucvtff1#
关系数据库通常不适合存储和检索大数据块。任何大于兆字节的数据都不应该存储在数据库中。核心数据使问题变得更糟。如果你直接访问数据库,你只能获取特定的列,但是当核心数据将行转换为对象,将列转换为属性时,你无法控制你获取的内容。当您将一个属性设置为
Allows External Storage
时,Core-data将在文件系统中存储一个大的数据blob,并且仅在您访问该属性时才加载它。这对于许多情况来说都是很好的,因为它很容易,并且可以极大地提高性能。问题是,访问这样的属性可能具有加载大文件的大量意外成本,而仅访问属性并不清楚。如果您存储了一个文件名,并显式地从磁盘加载文件的第二步,那么它将是在加载数据时。此外,如果这些数据可以从互联网恢复(例如,它们是从imageURL下载的图像),那么在核心数据之外管理可能会更好,因为你可以管理一个缓存,这将是很难做到的核心数据。
hlswsv352#
CoreData的观察行为可能由以下原因引起:
1.内存Map文件
1.惰性加载
当您简单地从CoreData中获取托管对象实体时,只有访问该实体时,它才真正从存储中读取(CoreData将其称为
faulting
)但是当您最终访问包含大数据的属性时,CoreData需要加载存储在外部文件中的数据。我认为CoreData在这里使用Memory Mapped文件做了一个聪明的优化。CoreData要求操作系统将文件的整个内容Map到内存区域,而不是逐字节(或逐块)从磁盘阅读到一个大的UInt 8数组,然后将数据对象封装在它周围。然后CoreData将围绕该内存区域 Package 一个Data对象。
创建内存Map文件时,操作系统不会将整个文件加载到内存中。操作系统将使用与
Virtual Memory
相同的分页机制。在任何时间点,操作系统将只加载正在读/写的文件的那些部分。但是从应用程序或您编写的任何代码的Angular 来看,Data对象的底层缓冲区的行为就好像整个文件都在缓冲区中一样。即使是Data对象也无法知道缓冲区不包含文件的所有字节。
有关更多信息,请阅读维基百科中的Memory Mapped Files,阅读苹果文档中的Mapping Files Into Memory