父/子MOC与异步盘保存获得冻结主队列在OSX [英] parent/child MOC with async disc saving got freeze main queue on osx

查看:423
本文介绍了父/子MOC与异步盘保存获得冻结主队列在OSX的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在尝试实施方案:



MOC1(PrivateQueue)-parent-> MOC2(MainQueue)-parent-> MOC3(PrivateQueue),PSC救



这里是初始化代码(MOC2&安培; MOC#:

  _writeManagedObjectContext = [[的NSManagedObjectContext页头] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
_writeManagedObjectContext.persistentStoreCoordinator =协调员;
_writeManagedObjectContext.undoManager =零;

_mainManagedObjectContext = [[的NSManagedObjectContext页头] initWithConcurrencyType:NSMainQueueConcurrencyType ];
_mainManagedObjectContext.undoManager =零;
_mainManagedObjectContext.parentContext = _writeManagedObjectContext;

这里是init MOC1:

  _mocSSchild = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
_mocSSchild.undoManager = nil;
_mocSSchild.parentContext = delegateMain.mainManagedObjectContext;

以下是保存:

  NSError * error = nil; 
[self.mocSSchild getsPermanentIDsForObjects:self.mocSSchild.insertedObjects.allObjects error:& error];

if(![self.mocSSchild save:& error]){
NSLog(@无法保存到FIRST数据存储:%@,[error localizedDescription]);
NSArray * detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors!= nil&& [detailedErrors count]> 0)
{
for(NSError * detailedError in detailedErrors)
{
NSLog @DetailedError:%@,[detailedError userInfo]);
}
}
else
{
NSLog(@%@,[error userInfo]);
}

}
AppDelegate * delegateMain =(AppDelegate *)[[NSApplication sharedApplication] delegate];
if([delegateMain.mainManagedObjectContext hasChanges]&&![delegateMain.mainManagedObjectContext save:& error]){NSLog(@无法保存到FIRST数据存储:%@,[error localizedDescription]) ;
NSArray * detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors!= nil&& [detailedErrors count]> 0)
{
for(NSError * detailedError in detailedErrors)
{
NSLog @DetailedError:%@,[detailedError userInfo]);
}
}
else
{
NSLog(@%@,[error userInfo]);
}
}
if([delegateMain.writeManagedObjectContext hasChanges]&&![delegateMain.writeManagedObjectContext save:& error]){NSLog(@无法保存到FIRST数据存储:%@,[error localizedDescription]);
NSArray * detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors!= nil&& [detailedErrors count]> 0)
{
for(NSError * detailedError in detailedErrors)
{
NSLog @DetailedError:%@,[detailedError userInfo]);
}
}
else
{
NSLog(@%@,[error userInfo]);
}
}

所有获取请求都放在右侧块当然,我从来不触摸mainManagedObjectContext来执行获取请求):

  __block NSError * error = nil; 
__block NSArray * findedResult = nil;
[self.mocSSchild performBlockAndWait:^ {
findedResult = [self.mocSSchild executeFetchRequest:fetchRequest error:& error];
}];

一次保存后,我在主队列中冻结(看起来像核心数据尝试在mainManagedObjectContext我不要求请求):

 调用图:
2633 Thread_803320 DispatchQueue_173:NSManagedObjectContext Queue b $ b + 2633开始(在libdyld.dylib)+ 1 [0x7fff8bb907e1]
+ 2633 main(在callsfreecall)+ 34 [0x10992c202]
+ 2633 NSApplicationMain(AppKit)+ 869 [0x7fff8a49cbd6]
+ 2633 - [NSApplication run](在AppKit中)+ 517 [0x7fff8a4f81a3]
+ 2633 - [NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:](in AppKit)+ 128 [0x7fff8a500df2]
+ 2633 _DPSNextEvent(in AppKit)+ 685 [0x7fff8a501533]
+ 2633 BlockUntilNextEventMatchingListInMode(在HIToolbox中)+ 62 [0x7fff8f356ae3]
+ 2633 ReceiveNextEventCommon(在HIToolbox中)+ 356 [0x7fff8f356c52]
+ 2633 RunCurrentEventLoopInMode(在HIToolbox中)+ 209 [0x7fff8f356eb4]
+ 2633 CFRunLoopRunSpecific(在CoreFoundation中)+ 290 [0x7fff9526c0e2]
+ 2633 __CFRunLoopRun(在CoreFoundation中)+ 1644 [0x7fff9526cb4c]
+ 2633 _dispatch_main_queue_callback_4CF在libdispatch.dylib中)+ 275 [0x7fff8c4c20c8]
+ 2633 _dispatch_client_callout(在libdispatch.dylib中)+ 8 [0x7fff8c4bd0b6]
+ 2633 _dispatch_barrier_sync_f_slow_invoke(在libdispatch.dylib中)+ 77 [0x7fff8c4c2a2d]
+ 2633 __82- [NSManagedObjectContext(_NestedContextSupport)executeRequest:withContext:error:] _ block_invoke_0(在CoreData中)+ 533 [0x7fff93d8b6c5]
+ 2633 - [NSManagedObjectContext countForFetchRequest:error:](在CoreData中)+ 1563 [0x7fff93d65ddb] b $ b + 2633 - [NSManagedObjectContext(_NestedContextSupport)executeRequest:withContext:error:](在CoreData中)+ 354(在CoreData中)+ 298 [0x7fff93d65f4a] [0x7fff93d576c2]
+ 2633 _perform(在CoreData中)+ 172 [0x7fff93d5787c]
+ 2633 _dispatch_barrier_sync_f_invoke(在libdispatch.dylib中)+ 39 [0x7fff8c4be723]
+ 2633 _dispatch_client_callout + 8 [0x7fff8c4bd0b6]
+ 2633 __82- [NSManagedObjectContext(_NestedContextSupport)executeRequest:withContext:error:] _ block_invoke_0(在CoreData中)+ 533 [0x7fff93d8b6c5]
+ 2633 - [NSManagedObjectContext countForFetchRequest:error:]在CoreData)+ 1563 [0x7fff93d65ddb]
+ 2633 - [NSManagedObjectContext(_NSInternalAdditions)_countWithNoChangesForRequest:error:](在CoreData中)+ 298 [0x7fff93d65f4a]
+ 2633 - [NSPersistentStoreCoordinator executeRequest:withContext:error:] (在CoreData中)+ 1138 [0x7fff93d10ba2]
+ 2633 - [_ PFLock lock](在CoreData中)+ 24 [0x7fff93cfe548]
+ 2633 pthread_mutex_lock(libsystem_c.dylib)+ 536 [0x7fff92797dfd]
+ 2633 __psynch_mutexwait(在libsystem_kernel.dylib)+ 10 [0x7fff938ca122]


解决方案>

首先,当你收到错误时,你需要做一些错误。现在你不做什么,不记录他们,不对他们做出反应,什么也不做。这不好。当出现错误时,您根本没有任何迹象。



其次,您将所有MOC保存在同一队列中。任何针对私有MOC的活动必须通过 -performBlock: -performBlockAndWait:方法在块内执行。



第三,没有指示你正在运行什么队列。



您的导入MOC应该在操作或运行异步的块中运行。导入MOC应该使用线程限制而不是私有MOC。一旦导入MOC完成其工作,它应该保存自己,然后向主队列指示应保存主MOC。



主MOC只能保存在主队列。当主MOC完成其保存时,它应该通过其 -performBlock:启动保存到顶级MOC。



更正错误结果的问题,并查看您正在进行的工作的队列。如果一切都在主队列上,那么父/子MOC不会解决你的问题。您需要重新思考在哪里执行的工作。



更新1



首先,通过监听来自呼叫的结果。如果调用返回 BOOL ,那么这是一个通过/失败。如果调用返回了别的东西,那么如果别的东西 nil 那么那就是失败,错误就会出现。



至于你的代码,没有它是不正确的。你有线程的问题,我提到。你直接触摸私人MOC,这是不正确的。



您的写入MOC只能通过 -performBlock: -performBlockAndWait:。在这种情况下,您应该使用 -performBlock:。你直接触摸它。这是坏的。



您的主要MOC只能在主队列或通过 -performBlock: -performBlockAndWait:



您正在以单身形式访问您的 appDelegate 。这是一个坏的代码气味。查找并开始使用依赖注入。



您的孩子moc设置为私有,当它应该是线程限制上下文,然后它应该只在线程上访问创建它,它理想地应该在NSOperation中。



简而言之,这里有很多核心概念。你需要了解父/子上下文如何工作,你需要更好地了解队列和线程,然后再尝试开发这样的代码。


I have currently try to implement scheme:

MOC1 (PrivateQueue) -parent-> MOC2 (MainQueue) -parent-> MOC3 (PrivateQueue), PSC to save

here is init code (MOC2 & MOC#:

_writeManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_writeManagedObjectContext.persistentStoreCoordinator = coordinator;
_writeManagedObjectContext.undoManager = nil;

_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_mainManagedObjectContext.undoManager = nil;
_mainManagedObjectContext.parentContext = _writeManagedObjectContext;

Here is init MOC1:

    _mocSSchild = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    _mocSSchild.undoManager = nil;
    _mocSSchild.parentContext = delegateMain.mainManagedObjectContext;

Here is a save:

    NSError *error = nil;
    [self.mocSSchild obtainPermanentIDsForObjects:self.mocSSchild.insertedObjects.allObjects error:&error];

    if (![self.mocSSchild save: &error]) {
    NSLog(@"Failed to save to FIRST data store: %@", [error localizedDescription]);
    NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
    if(detailedErrors != nil && [detailedErrors count] > 0)
    {
        for(NSError* detailedError in detailedErrors)
        {
            NSLog(@"  DetailedError: %@", [detailedError userInfo]);
        }
    }
    else
    {
        NSLog(@"  %@", [error userInfo]);
    }

    }
    AppDelegate *delegateMain = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    if ([delegateMain.mainManagedObjectContext hasChanges] && ![delegateMain.mainManagedObjectContext save: &error]) {        NSLog(@"Failed to save to FIRST data store: %@", [error localizedDescription]);
    NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
    if(detailedErrors != nil && [detailedErrors count] > 0)
    {
        for(NSError* detailedError in detailedErrors)
        {
            NSLog(@"  DetailedError: %@", [detailedError userInfo]);
        }
    }
    else
    {
        NSLog(@"  %@", [error userInfo]);
    }
}
    if ([delegateMain.writeManagedObjectContext hasChanges] && ![delegateMain.writeManagedObjectContext save: &error]) {        NSLog(@"Failed to save to FIRST data store: %@", [error localizedDescription]);
    NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
    if(detailedErrors != nil && [detailedErrors count] > 0)
    {
        for(NSError* detailedError in detailedErrors)
        {
            NSLog(@"  DetailedError: %@", [detailedError userInfo]);
        }
    }
    else
    {
        NSLog(@"  %@", [error userInfo]);
    }
}

All fetch requests is placed in right block (and of course i never touch mainManagedObjectContext to execute fetch requests):

    __block NSError *error = nil;
    __block NSArray *findedResult = nil;
    [self.mocSSchild performBlockAndWait:^{
        findedResult = [self.mocSSchild executeFetchRequest:fetchRequest error:&error];
    }];

After one save i have freezing in main queue (looks like core data try to execute fetch request on mainManagedObjectContext where i don't do requests):

Call graph:
    2633 Thread_803320   DispatchQueue_173: NSManagedObjectContext Queue  (serial)
    + 2633 start  (in libdyld.dylib) + 1  [0x7fff8bb907e1]
    +   2633 main  (in callsfreecall) + 34  [0x10992c202]
    +     2633 NSApplicationMain  (in AppKit) + 869  [0x7fff8a49cbd6]
    +       2633 -[NSApplication run]  (in AppKit) + 517  [0x7fff8a4f81a3]
    +         2633 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]  (in AppKit) + 128  [0x7fff8a500df2]
    +           2633 _DPSNextEvent  (in AppKit) + 685  [0x7fff8a501533]
    +             2633 BlockUntilNextEventMatchingListInMode  (in HIToolbox) + 62  [0x7fff8f356ae3]
    +               2633 ReceiveNextEventCommon  (in HIToolbox) + 356  [0x7fff8f356c52]
    +                 2633 RunCurrentEventLoopInMode  (in HIToolbox) + 209  [0x7fff8f356eb4]
    +                   2633 CFRunLoopRunSpecific  (in CoreFoundation) + 290  [0x7fff9526c0e2]
    +                     2633 __CFRunLoopRun  (in CoreFoundation) + 1644  [0x7fff9526cb4c]
    +                       2633 _dispatch_main_queue_callback_4CF  (in libdispatch.dylib) + 275  [0x7fff8c4c20c8]
    +                         2633 _dispatch_client_callout  (in libdispatch.dylib) + 8  [0x7fff8c4bd0b6]
    +                           2633 _dispatch_barrier_sync_f_slow_invoke  (in libdispatch.dylib) + 77  [0x7fff8c4c2a2d]
    +                             2633 __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0  (in CoreData) + 533  [0x7fff93d8b6c5]
    +                               2633 -[NSManagedObjectContext countForFetchRequest:error:]  (in CoreData) + 1563  [0x7fff93d65ddb]
    +                                 2633 -[NSManagedObjectContext(_NSInternalAdditions) _countWithNoChangesForRequest:error:]  (in CoreData) + 298  [0x7fff93d65f4a]
    +                                   2633 -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]  (in CoreData) + 354  [0x7fff93d576c2]
    +                                     2633 _perform  (in CoreData) + 172  [0x7fff93d5787c]
    +                                       2633 _dispatch_barrier_sync_f_invoke  (in libdispatch.dylib) + 39  [0x7fff8c4be723]
    +                                         2633 _dispatch_client_callout  (in libdispatch.dylib) + 8  [0x7fff8c4bd0b6]
    +                                           2633 __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0  (in CoreData) + 533  [0x7fff93d8b6c5]
    +                                             2633 -[NSManagedObjectContext countForFetchRequest:error:]  (in CoreData) + 1563  [0x7fff93d65ddb]
    +                                               2633 -[NSManagedObjectContext(_NSInternalAdditions) _countWithNoChangesForRequest:error:]  (in CoreData) + 298  [0x7fff93d65f4a]
    +                                                 2633 -[NSPersistentStoreCoordinator executeRequest:withContext:error:]  (in CoreData) + 1138  [0x7fff93d10ba2]
    +                                                   2633 -[_PFLock lock]  (in CoreData) + 24  [0x7fff93cfe548]
    +                                                     2633 pthread_mutex_lock  (in libsystem_c.dylib) + 536  [0x7fff92797dfd]
    +                                                       2633 __psynch_mutexwait  (in libsystem_kernel.dylib) + 10  [0x7fff938ca122]

解决方案

First, you need to do something with errors when you receive them. Right now you are not doing anything, not logging them, not reacting to them, nothing. This is bad. When an error occurs you have no indication of it at all. You just go onto the next step.

Second, you are saving all of your MOCs on the same queue. Any activity against a private MOC must be performed inside of a block via the -performBlock: or -performBlockAndWait: methods.

Third, there is no indication what queue you are running on.

Your import MOC should be running inside of an operation or a block running async. The import MOC should be using thread confinement instead of being a private MOC. Once the import MOC has completed its work it should save itself and then indicate to the main queue that the main MOC should be saved.

The main MOC should only be saved on the main queue. When the main MOC has completed its save then it should fire off a save to the top level MOC via its -performBlock:.

Correct your issues with the error results and take a look at what queues you are doing the work on. If everything is on the main queue then parent/child MOCs is not going to solve your problem. You need to rethink what work is being performed where.

Update 1

First, you can detect the errors by listening to the results from the calls. If the call returns a BOOL then that is a pass/fail. If the call returns something else then if that something else is nil then that is a failure and the error will be present.

As for your code, no it is not correct. You have threading issues as I mentioned. You are touching a private MOC directly, that is incorrect. There are probably other errors but I cannot see them based on this code.

Your "writing" MOC should only be touched via a -performBlock: or -performBlockAndWait:. In this case you should be using a -performBlock:. You are touching it directly. That is bad.

Your main MOC should only be touched on the main queue or via a -performBlock:/-performBlockAndWait:. You appear to be touching it directly in this code.

You are accessing your appDelegate as a singleton. This is a bad code smell. Look up and start using dependency injection.

Your child moc is set up as a private when it should be a thread confined context and then it should only be accessed on the thread that created it which ideally should be in an NSOperation.

In short, there is a lot of core concepts that you are missing here. You need to understand how parent/child contexts work and you need to have a better understanding of queues and threads before you attempt to develop code like this.

这篇关于父/子MOC与异步盘保存获得冻结主队列在OSX的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆