在使用后台线程上的Core Data在OS X中导入大型.csv文件时遇到了一些麻烦。
我的简化数据模型是一个数据集,它与许多条目有很多关系。每个条目都是.csv文件中的一行(其中包含一些我为了简洁而遗漏的属性)。根据我所读到的关于有效导入大量数据的内容,以及如何使进度指示器正常工作,我创建了一个托管对象上下文,以便进行导入。
我遇到两件事有困难:
我需要在导入结束时继续引用新数据集,因为我需要在弹出窗口中选择它。这将在主线程中完成,但为了提高效率(并使我的NSProgressIndicator工作),新的数据集在后台线程上创建,背景为MOC。
根据我的阅读,批量导入,以便后台MOC保存和重置,是阻止导入占用过多内存的最佳方法。到目前为止情况并非如此 - 看起来即使是数十兆字节的文件也会使用内存演出。此外,一旦我重置了导入MOC,它就无法找到inDataset的数据,因此无法在所有后续条目和数据集之间创建关系。
我已经发布了以下简化代码。我尝试过refreshObject:mergeChanges,没有很好的结果。谁能指出我做错了什么?
let inFile = op.URL
dispatch_async(dispatch_get_global_queue(priority, 0)) {
//create moc
let inMOC = NSManagedObjectContext()
inMOC.undoManager = nil
inMOC.persistentStoreCoordinator = self.moc.persistentStoreCoordinator
var inDataset : inDataset = Dataset(entity: NSEntityDescription.entityForName("Dataset", inManagedObjectContext: inMOC)!, insertIntoManagedObjectContext: inMOC)
//set up NSProgressIndicator here, removed for clarity
let datasetID = inDataset.objectID
mocDataset = self.moc.objectWithID(datasetID) as! Dataset
let fileContents : String = (try! NSString(contentsOfFile: inFile!.path!, encoding: NSUTF8StringEncoding)) as String
let fileLines : [String] = fileContents.componentsSeparatedByString("\n")
var batchCount : Int = 0
for thisLine : String in fileLines {
let newEntry : Entry = Entry(entity: NSEntityDescription.entityForName("Entry", inManagedObjectContext: inMOC)!, insertIntoManagedObjectContext: inMOC)
//Read in attributes of this entry from this line, removed here for brevity
newEntry.setValue("Entry", forKey: "type")
newEntry.setValue(inDataset, forKey: "dataset")
inDataset.addEntryObject(newEntry)
dispatch_async(dispatch_get_main_queue()) {
self.progInd.incrementBy(1)
}
batchCount++
if(batchCount > 1000){
do {
try inMOC.save()
} catch let error as NSError {
print(error)
} catch {
fatalError()
}
batchCount = 0
inMOC.reset()
inDataset = inMOC.objectWithID(datasetID) as! Dataset
// fails here, does not seem to be able to find the data associated with inDataset
}
}// end of loop for reading lines
//save whatever remains after last batch save
do {
try inMOC.save()
} catch let error as NSError {
print(error)
} catch {
fatalError()
}
inMOC.reset()
dispatch_async(dispatch_get_main_queue()) {
//This is done on main queue after background queue has read all line
// I thought the statement just below would refresh mocDataset, but no luck
self.moc.refreshObject(mocDataset, mergeChanges: true)
//new dataset selected from popup
let datafetch = NSFetchRequest(entityName: "Dataset")
let datasets : [Dataset] = try! self.moc.executeFetchRequest(datafetch) as! [Dataset]
self.datasetController.addObjects(datasets)
let mocDataset = self.moc.objectWithID(datasetID) as! Dataset
//fails here too, mocDataset object has data as a fault
let nDarray : [Dataset] = [mocDataset]
self.datasetController.setSelectedObjects(nDarray)
}
}