将 100MB XML 文件导入 iOS 核心数据模型
Posted
技术标签:
【中文标题】将 100MB XML 文件导入 iOS 核心数据模型【英文标题】:Importing 100MB XML file into iOS Core Data Model 【发布时间】:2011-05-11 13:47:27 【问题描述】:在我的应用程序中,我需要将一个 100MB 的 xml 文件导入核心数据模型。
到目前为止,我已经导入了一个 100KB 的 xml 文件,一切正常。但是,不确定导入 100MB 的 xml 需要多长时间。一会儿我会运行它。你认为这种方法好吗?
谢谢
NSManagedObjectContext * context = [self managedObjectContext];
// Delete all documents
NSFetchRequest * fetch = [[[NSFetchRequest alloc] init] autorelease];
[fetch setEntity:[NSEntityDescription entityForName:@"Document" inManagedObjectContext:context]];
NSArray * result = [context executeFetchRequest:fetch error:nil];
for (id basket in result)
[context deleteObject:basket];
//Insert documents
TBXML * tbxml = [[TBXML tbxmlWithXMLFile:@"categ_small.xml"] retain];
TBXMLElement * root = tbxml.rootXMLElement;
TBXMLElement * doc = [TBXML childElementNamed:@"doc" parentElement:root];
do
TBXMLElement * idDoc = [TBXML childElementNamed:@"id" parentElement:doc];
TBXMLElement * titleDoc = [TBXML childElementNamed:@"title" parentElement:doc];
TBXMLElement * descriptionDoc = [TBXML childElementNamed:@"description" parentElement:doc];
TBXMLElement * time = [TBXML childElementNamed:@"time" parentElement:doc];
TBXMLElement * tags = [TBXML childElementNamed:@"tags" parentElement:doc];
TBXMLElement * geo = [TBXML childElementNamed:@"geo" parentElement:doc];
TBXMLElement * event = [TBXML childElementNamed:@"event" parentElement:doc];
TBXMLElement * user = [TBXML childElementNamed:@"user" parentElement:doc];
TBXMLElement * categ = [TBXML childElementNamed:@"categ" parentElement:doc];
NSManagedObject *newDocument = [NSEntityDescription
insertNewObjectForEntityForName:@"Document"
inManagedObjectContext:context];
[newDocument setValue:[TBXML textForElement:idDoc] forKey:@"idDoc"];
[newDocument setValue:[TBXML textForElement:titleDoc] forKey:@"titleDoc"];
[newDocument setValue:[TBXML textForElement:descriptionDoc] forKey:@"descriptionDoc"];
[newDocument setValue:[TBXML textForElement:time] forKey:@"time"];
[newDocument setValue:[TBXML textForElement:tags] forKey:@"tags"];
[newDocument setValue:[TBXML textForElement:geo] forKey:@"geo"];
[newDocument setValue:[TBXML textForElement:event] forKey:@"event"];
[newDocument setValue:[TBXML textForElement:user] forKey:@"user"];
[newDocument setValue:[TBXML textForElement:categ] forKey:@"categ"];
while ((doc = doc->nextSibling));
更新 这是一次性操作,仅在模拟器中运行,不会与最终应用程序一起部署。
【问题讨论】:
不,它几乎所有东西,但不是很好。 @JustSid 好的,谢谢!你介意分享一下原因吗? 如果只在模拟器上运行,为什么不试试呢? @paulbailey 因为这需要一段时间。无论如何,我已经尝试过了,过了一段时间我得到了一个糟糕的内存访问错误(虽然使用一个小文件它完美地工作)。 【参考方案1】:我会执行以下步骤:
使用 ruby 或 php 编写转换器 XML->SQLite,或者如果您不是脚本语言的朋友,请查看 SQLite Manager,它是一个用于管理 SQLite 数据库的 Firefox 插件,它能够导入 XML。
按照本教程预填充核心数据以进行发布:How To Preload/Import Existing Data
【讨论】:
【参考方案2】:将这样大小的文档放在常驻内存中是一种让您的应用被系统杀死的快速方法。
要在资源有限的系统上导入如此大的 XML 文档,您需要流式解析,这将保持驻留内存占用较小。这里有很多选择。 Apple 的 NSXMLParser 会做到这一点,尽管它不如某些替代品快。 (这些替代方案的一个很好的概述是here。)
对于实现,我建议解析NSOperation
子类,定期通知主线程进度更新。这将防止 UI 在您解析任意大的文档时阻塞。
使用NSEntityDescription
的方式将为 XML 文件中的每个 doc 兄弟创建单独的自动释放对象,这是另一种快速解决内存不足的方法。查看 Apple 的 Efficiently Importing Data 文档以了解正确的方法。
【讨论】:
谢谢。 (1) 我实际上选择了 TBXML 解析器,因为根据您传递给我的链接(我已经阅读过),它是最快的。 (2)对于“流式解析”,您的意思是将数据解析并直接存储到核心模型中? (3) 忘了说解析明明只发生一次,以后不会再用xml文件了。 所以,如果你的意思是我应该使用 SAX 解析器而不是 DOM 解析器,我猜 TBXML(DOM 解析器)对于低内存设备来说不是一个好的选择。你能确认一下吗? 它是最快的,但是它使用 DOM,所以整个东西都在常驻内存中。这对于您经常引用的较小文档来说很好,但对于处理非常大的文档来说很糟糕,尤其是在资源有限的系统上。如果您只需要导入一次并发送预填充的数据库,Nick 的建议就是您要遵循的建议。【参考方案3】:如果这是用于构建 CoreData 或 SQLite 数据库以与您的应用程序一起分发的一次性过程,它可能会工作,但只能在模拟器上工作。但是,如果您需要在设备上执行此操作,则需要切换到流解析器。
【讨论】:
是的,我忘了提到确实只适用于模拟器的一次性过程。这肯定不是最有效的方法,但你认为我可以只运行这段代码吗?或者我可以做一些优化? 它应该可以工作。模拟器不是模拟器,因此它应该可以访问您 Mac 上的内存,而不是任何特定 ios 设备上的有限内存。 相反,只有当文件很大、很奇怪时,我才会获得糟糕的内存访问。有了小文件,一切都很好。我会检查文件...以上是关于将 100MB XML 文件导入 iOS 核心数据模型的主要内容,如果未能解决你的问题,请参考以下文章
如何将预先存在的 sqlite 文件导入核心数据 iOS 7.1
如何通过 iOS 6 中的 RESTkit 2.0 将 JSON 中的 base64 图像导入核心数据二进制文件?