Core data的batch update
关于Core data最大的抱怨是缺乏批处理操作。比如RSS阅读器这种需要在众多entities上操作的应用,没有批处理操作就必须将所有东西装入内存,然后手动设置必要的attributes.
通常你会这么操作:
NSFetchRequest *fetchAllRSSItems = [NSFetchRequest fetchRequestWithEntityName:[RSSItem entityName]]; NSError *fetchError; NSArray *results = [self.managedObjectContext executeFetchRequest:fetchAllRSSItems error:&fetchError]; if (results == nil) { NSLog(@"@"Error: %@", [fetchError localizedDescription]); } else { for (RSSItem *rssItem in results) { rssItem.read = [NSNumber numberWithBool:YES]; } [self saveManagedObjectContext]; }
如果你使用一个主线程的managed object context, 你将会阻塞UI。你可以通过一个私有的managed object context来消除阻塞,但是这种操作仍然是耗时的。
在内存有限的设备,如iPhone上,将所有的entities放进内存甚至有可能做不到。为了解决内存问题,必须分批操作,但是这样做并不会加快操作。这种分批操作只好向用户显示一个进度条。
在iOS8和OS X Yosemite上,你再也不用将所有entities装进内存,手动设置attributes.
NSBatchUpdateRequest *batchRequest = [NSBatchUpdateRequest batchUpdateRequestWithEntityName:[RSSItem entityName]]; batchRequest.propertiesToUpdate = @{NSStringFromSelector(@selector(read)):[NSNumber numberWithBool:YES]}; batchRequest.resultType = NSStatusOnlyRequestType; // NSStatusOnlyResultType is the default batchRequest.affectedStores = @[...]; // Optional, stores this request should be sent to batchRequest.predicate = [NSPredicate predicateWithFormat:@"..."]; // Optional, same type of predicate you use on NSFetchRequest NSError *requestError; NSBatchUpdateResult *result = (NSBatchUpdateResult *)[self.managedObjectContext executeRequest:batchRequest error:&requestError]; if (result == nil) { NSLog(@"Error: %@", [requestError localizedDescription]); } else { // Batch update succeeded }
当执行executeRequest:error:时,它将跨过managedObjectContext,直接update persistent store. 由于在context上没有任何改变的反映,所以该过程不会执行验证机制,所以必须小心。
如果需要在batch 操作完成之后update UI,那么可以讲batch request的resultType设为NSUpdatedObjectIDsResultType. 一旦有了IDs, 就可以通过主moc获取entities来更新UI.
如果并不关心实际的entities,而是想知道change是否发生,可以将resultType设置为NSUpdateObjectsCountResultType.当批处理完成之后,结果属性中会有一个NSNumber的变量存储改变的entities的数量。