核心数据和多线程
我读过 Marcus Zarra 在他的《Core Data》书中关于多线程的章节,并仔细研究了他的示例代码。但他的代码和我在其他地方找到的其他代码似乎都集中在不需要相互了解的后台进程中。这些示例非常适合导入树结构 - 但它们并没有解决更一般(复杂)结构(例如有向无环图)的导入问题。
就我而言,我正在尝试解析 C++ 类层次结构,并希望使用尽可能多的 NSOperations。我想为每个遇到的类创建一个 NSManagedObject 实例,并且我想在保存时合并不同的 NSManagedObjectContext。
顺便说一句:我能够使用单个 NSOperation 来迭代文件并一次解析一个文件。在此实现中,在主线程的 MOC 上调用 -mergeChangesFromContextDidSaveNotification: 的 -mergeChanges: 方法效果很好。
但理想情况下,我会让一个 NSOperation 迭代源文件并生成 NSOperations 来解析每个文件。我尝试了几种方法 - 但似乎无法做到正确。最有希望的是让每个 NSOperation 观察 NSManagedObjectContextDidSaveNotification。使用 -mergeChanges: 看起来像这样:
- (void) mergeChanges:(NSNotification *)notification
{
// If locally originated, then trigger main thread to merge.
if ([notification object] == [self managedObjectContext])
{
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
return;
}
// If not locally originated, then flag need to merge with in this NSOperation's thread.
[self setNeedsToMerge:YES];
[self setMergeNotification:notification];
}
本质上,解析 NSOperation 的 main() 定期检查 ivar 'needsToMerge'。如果为 true,则使用缓存的 NSNotifications 在本地 MOC 上调用 -mergeChangesFromContextDidSaveNotification: 。然后needsToMerge被重置。如果通知源自本地,则主线程被告知在其 MOC 上执行 -mergeChangesFromContextDidSaveNotification: 。
我确信这不起作用以及为什么我得到这个是有充分理由的:
警告:正在取消呼叫 - objc 代码 在当前线程的堆栈上使得 这不安全。
我还尝试使用 NSPerisentStoreCoordinator 的锁来控制访问 - 但如果在调用 NSManagedObjectContext 的 -save: 方法期间保持它,则会出现问题,因为 -save: 将通知感兴趣的观察者保存事件,而 -mergeChangesFromContextDidSaveNotification: 似乎会阻止尝试获取PSC 的锁。
看起来这应该容易得多。
I have read Marcus Zarra's chapter on multithreading in his Core Data book and have looked fairly closely at his sample code. But his code and others that I have found elsewhere seem to be focused in background processes that do not need to be aware of each other. These examples are good for importing a tree structure - but they do not address the importing of a more general (complex) structure, like a directed acyclic graph.
In my case, I am trying to parse a C++ class hierarchy and would like to use as many NSOperations as possible. I would like to create an NSManagedObject instance for each encountered class and I would like to merge different NSManagedObjectContexts whenever one is saved.
As an aside: I am able to get things working with a single NSOperation that iterates of files and parse one at a time. In this implementation, the -mergeChanges: approach that calls -mergeChangesFromContextDidSaveNotification: on main thread's MOC works well.
But ideally, I would have one NSOperation iterate over source files and spawn NSOperations to parse each file. I have tried several approaches - but can't seem to get it right. The most promising was to have each NSOperation observing NSManagedObjectContextDidSaveNotification. With -mergeChanges: looking like this:
- (void) mergeChanges:(NSNotification *)notification
{
// If locally originated, then trigger main thread to merge.
if ([notification object] == [self managedObjectContext])
{
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
return;
}
// If not locally originated, then flag need to merge with in this NSOperation's thread.
[self setNeedsToMerge:YES];
[self setMergeNotification:notification];
}
Essentially, the parsing NSOperation's main() checked ivar 'needsToMerge' periodically. If it was true, then -mergeChangesFromContextDidSaveNotification: was called on local MOC with cached NSNotifications. And then needsToMerge was reset. If notification had originated locally, then main thread was told to perform -mergeChangesFromContextDidSaveNotification: on its MOC.
I am sure that there is a good reason why this didn't work and why I get this:
warning: Cancelling call - objc code
on the current thread's stack makes
this unsafe.
I have also tried to use NSPeristentStoreCoordinator's lock to control access - but this is problematic if it is held during a call to NSManagedObjectContext's -save: method because -save: will notify interested observers of save event and -mergeChangesFromContextDidSaveNotification: appears to block trying to acquire PSC's lock.
It just seems that this should be a lot easier.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我想我也遇到了同样的问题,这是我解决它的方法:
创建一个自定义 NSOperation 类,在其中定义:
在保存上下文之前首先在 NSOperation 主方法中应用所有请求的更改:
请注意,我使用了锁,这是因为 NSMutableArray 是不是线程安全的,我想安全地访问changeNotifications。
ChangeNotifications 是存储在保存上下文之前需要应用的所有更改的数组。
这是您的合并方法,经过修改,以便 NSOperation 需要合并的所有更改都使用正确的线程进行合并。请注意,此方法是由 NSOperation 之外的其他线程调用的,因此您需要锁定对 self.changeNotifications 的访问
希望这会有所帮助!此方法并非 100% 坚如磐石,在某些情况下,更改通知可能到达得太晚。在这种情况下,上下文保存方法将返回错误,您必须重新加载 NSManagedObject 并再次保存它。
如果需要更多详细信息,请告诉我。
I think I hade the same problem and here is how I solve it:
Create a custom NSOperation Class where you define:
In your NSOperation main method before saving the context first apply all requested changes:
Note that I used a lock, this because NSMutableArray is not thread safe and I want to safely access changeNotifications.
changeNotifications is the array where all changes that need to be applied before saving the context are stored.
And here is your merge method, modified so that all changes that need to be merged by your NSOperation are merged using the correct thread. Note that this methods is called by other threads than your NSOperation one, therefore you need to lock the access to self.changeNotifications
Hope this help! This method is not 100% rock solid there may be some cases where a change notification may arrive too late. In that case the context save method will return an error and you have to reload the NSManagedObject and save it again.
In case more details are needed please let me know.
这两段代码现在在我的应用程序中正常工作:
当然, ManagedObjectContext 意味着:
如果您需要在从主 moc 进行更新之前删除某些内容,请小心。我有疯狂的时间和很多难以调试的错误,但我知道我无法在主线程中使用 moc,而在任何地方处理具有相同内容的其他更改。
This two peace of code working now correctly in my application:
Of course, managedObjectContext mean:
Be careful if u need to remove something before u make updates from main moc. I has crazy time and a lot of hard to debug errors while i understand that i can't using moc from main thread while anywhere processing other changes with same content.