带有 Magical Record 的导入记录不会立即显示在 tableView 中
Posted
技术标签:
【中文标题】带有 Magical Record 的导入记录不会立即显示在 tableView 中【英文标题】:Imported records with Magical Record not immediately displayed in tableView 【发布时间】:2014-01-07 14:02:58 【问题描述】:我的应用程序从第 3 方数据库(使用 AFNetworking)下载 XML 文件,然后使用 NSXMLParser
提取我需要的信息,并使用子节点“随时随地”创建记录。我正在使用 MagicalRecord 与 CoreData 进行通信。下载和解析部分工作正常,但是在我完成后,并不是所有条目都在我的表中可见,我猜刷新我的 tableView 时 MR 还没有完成。如果我离开 tableView 并返回,则会显示所有记录。
我使用dispatch_group
来确保在完成所有下载和导入任务之前不会更新 UI。使用NSLog
我已经确认dispatch_group_notify
块中的代码确实在所有下载和解析完成后执行。
关于如何修复代码以使所有记录立即显示在表中的任何建议?
这里有一些代码:
-(void) importRecords
dispatch_group_t dispatchGroup = dispatch_group_create();
for (NSString *s in self.newRecords)
dispatch_group_enter(dispatchGroup);
NSData *data = [self downloadDataforRecord: s]; // using AFNetworking
if (data)
[self importData: data];
dispatch_group_leave(dispatchGroup);
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^
// done with all the downloading and parsing
// now update the UI
self.records = [NSMutableArray arrayWithArray: [Record MR_findAllSortedBy: @"id" ascending: ascending]];
[self.tableView reloadData];
);
- (void) importData:(NSData *) data
[MagicalRecord saveWithBlock: ^(NSManagedObjectContext *localContext)
Root *rootObject = [[Root alloc] init];
rootObject.context = localContext;
NSXMLParser *parser = [[NSXMLParser alloc] initWithData: data];
parser.delegate = rootObject;
[parser setShouldProcessNamespaces: YES];
[parser parse];
// during the parse, records and subnodes are created using:
// [Record MR_createInContext: context];
// where context is the same as in the rootObject above
completion: ^(BOOL success, NSError *error)
if (error)
// show error alert
else if (success)
];
【问题讨论】:
【参考方案1】:为什么你不能如下使用你的完成处理程序
completion: ^(BOOL success, NSError *error)
if (error)
// show error alert
else if (success)
self.records = [NSMutableArray arrayWithArray: [Record MR_findAllSortedBy: @"id" ascending: ascending]];
[[NSOperationQueue mainQueue] addOperationBlock:^
[self.tableView reloadData];
];
];
而且您不需要实现 dispatch_group_t。
【讨论】:
这是个好建议。我没有尝试过,因为MR_findAllSortedBy:
和 reloadData
将为我导入的每条记录调用。我的想法是在所有导入之后再做。我今晚会试试这个。
此解决方案有效,但正如我所怀疑的那样,表会为每条记录重新加载,这使它看起来不连贯。我也不能使用 HUD,因为没有办法知道什么时候一切都结束了。
我想知道 self.records 包含什么,它会包含解析器完成解析后的所有行还是只包含一条记录?
它包含所有行,它是我的tableView的数据源。【参考方案2】:
您的主要问题是您在调度块内进行调度。因此,您的 dispatch_group_notify 块在您的导入完成之前被调用。如果您仍想使用调度组来发出导入完成的信号,则需要阻止外部块,通常,我将使用 dispatch_semaphore_t 来阻止,如下所示:
dispatch_semaphore_t waitForSave = dispatch_semaphore_create(0);
dispatch_group_async(save_group, save_queue, ^
//do your import here
dispatch_semaphore_signal(waitForSave);
);
dispatch_semaphore_wait(waitForSave, DISPATCH_TIME_FOREVER);
// trigger your UI update here.
我建议这种方法的原因是因为您正在使用 [MagicalRecord saveWithBlock:],如果您查看源代码,它会将那个块分派到另一个后台队列中。该队列很可能与您使用的队列不同(因为 MagicalRecord 创建了自己的私有保存队列)。因此,您基本上必须等待操作完成并在保存操作实际完成时执行 UI 更新。
【讨论】:
我可以/应该使用更合适的替代 MR_save* 方法吗?例如MR_saveToPersistentStoreAndWait
或saveWithBlockAndWait:
?
潜在地,您可以使用 MR_saveToPersistentStoreAndWait,但您可能仍然遇到线程问题。从我所见,底线是你在一个调度中调度,这意味着你期望等待的那个完成得太早了。我经常对自己这样做,如果我无法重构调度,这通常是我解决它的方法。
我仍在苦苦挣扎的是,我在 for 循环中执行了一堆导入,并且只有在所有导入完成后,我的 UI 才需要更新。我不知道如何在 for 循环中使用上面的 dispatch_semaphore_t
示例代码?【参考方案3】:
这就是我最终要做的。
我在 MagicalRecord 保存块的成功块中放置了一个计数器,并为每次导入增加它。一旦计数器等于 newRecords 的数量,我就知道最后一个已导入,我可以更新我的 UI。
也许不是最有效和最优雅的方式,但现在它有效。也许在阅读更多之后,我想出了一个 GCD 解决方案。
【讨论】:
以上是关于带有 Magical Record 的导入记录不会立即显示在 tableView 中的主要内容,如果未能解决你的问题,请参考以下文章
CoreData + Magical Record 运行选择查询
导入与 Core Data 和 Magical Record 的关系