2

使用 performBlock 在同一个私有队列 NSManagedObjectContext 上同时调用 countForFetchRequest 两次时,我的 iOS 应用程序崩溃。

我像这样设置我的 childContext

_childContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_childContext setParentContext:self.managedObjectContext];

这是我调用 countForFetchRequest 的 performBlock

[self.childContext performBlock:^{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"History"];

    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"url == %@", url];

    NSError *error = nil;

    NSUInteger num = [self.childContext countForFetchRequest:fetchRequest error:&error];

    if(error != nil){
        NSLog(@"Error getting count for history url %@ %@", url, error);
        return;
    }

    if(num > 0){ // Already have this in the history, don't re-add it
        return;
    }

    History *history = (History *)[NSEntityDescription insertNewObjectForEntityForName:@"History" inManagedObjectContext:self.childContext];

    history.url = url;
    history.title = title;

    if(![self.childContext save:&error]){
        NSLog(@"Error occurred saving history item %@ %@ %@", title, url, error);
    }
}];

这是崩溃日志:

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib          0x3a427350 __pthread_kill + 8
1   libsystem_c.dylib               0x3a39e11e pthread_kill + 54
2   libsystem_c.dylib               0x3a3da96e abort + 90
3   libc++abi.dylib                 0x39978d4a abort_message + 70
4   libc++abi.dylib                 0x39975ff4 default_terminate() + 20
5   libobjc.A.dylib                 0x39f29a74 _objc_terminate() + 144
6   libc++abi.dylib                 0x39976078 safe_handler_caller(void (*)()) + 76
7   libc++abi.dylib                 0x39976110 std::terminate() + 16
8   libc++abi.dylib                 0x39977594 __cxa_rethrow + 84
9   libobjc.A.dylib                 0x39f299cc objc_exception_rethrow + 8
10  CoreData                        0x31e868e0 -[NSManagedObjectContext(_NSInternalAdditions) _countWithNoChangesForRequest:error:] + 764
11  CoreData                        0x31e833fe -[NSManagedObjectContext countForFetchRequest:error:] + 1062
12  CoreData                        0x31e92470 __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0 + 460
13  libdispatch.dylib               0x3a345d26 _dispatch_barrier_sync_f_slow_invoke + 82
14  libdispatch.dylib               0x3a3404b4 _dispatch_client_callout + 20
15  libdispatch.dylib               0x3a3451b8 _dispatch_main_queue_callback_4CF$VARIANT$mp + 220
16  CoreFoundation                  0x3205cf36 __CFRunLoopRun + 1286
17  CoreFoundation                  0x31fcfeb8 CFRunLoopRunSpecific + 352
18  CoreFoundation                  0x31fcfd44 CFRunLoopRunInMode + 100
19  GraphicsServices                0x35b992e6 GSEventRunModal + 70
20  UIKit                           0x33ee52fc UIApplicationMain + 1116
21  Accountable2You Mobile          0x000e5d28 0xe4000 + 7464
22  Accountable2You Mobile          0x000e5cc4 0xe4000 + 7364

Thread 1 name:  Dispatch queue: NSManagedObjectContext Queue
Thread 1:
0   libsystem_kernel.dylib          0x3a416f04 semaphore_wait_trap + 8
1   libdispatch.dylib               0x3a3462fc _dispatch_thread_semaphore_wait$VARIANT$mp + 8
2   libdispatch.dylib               0x3a34487c _dispatch_barrier_sync_f_slow + 96
3   CoreData                        0x31e82df2 _perform + 166
4   CoreData                        0x31e921c6 -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] + 238
5   CoreData                        0x31e86770 -[NSManagedObjectContext(_NSInternalAdditions) _countWithNoChangesForRequest:error:] + 396
6   CoreData                        0x31e833fe -[NSManagedObjectContext countForFetchRequest:error:] + 1062
7   Accountable2You Mobile          0x000ee7be 0xe4000 + 42942
8   CoreData                        0x31e86072 developerSubmittedBlockToNSManagedObjectContextPerform_privateasync + 66
9   libdispatch.dylib               0x3a344eca _dispatch_queue_drain$VARIANT$mp + 138
10  libdispatch.dylib               0x3a344dbc _dispatch_queue_invoke$VARIANT$mp + 36
11  libdispatch.dylib               0x3a34591a _dispatch_root_queue_drain + 182
12  libdispatch.dylib               0x3a345abc _dispatch_worker_thread2 + 80
13  libsystem_c.dylib               0x3a375a0e _pthread_wqthread + 358
14  libsystem_c.dylib               0x3a3758a0 start_wqthread + 4

我是否正确使用了 performBlock?

编辑:更多细节

performBlock 在 webViewDidFinishLoad:webView 委托方法中被调用。我有多个 UIWebViews,当它们完成加载时,它们调用委托方法,该方法又调用 performBlock 以查看它是否需要将 url 添加到核心数据数据库。

4

1 回答 1

0

您可以将导致崩溃的方法封装在:

@synchronized(self) {
    [object yourMethod];
}

这将确保即使在多线程时,内部的代码也将一次只运行一次......其他线程将只需要等待。坦率地说,这只有在崩溃的原因是并发时才有效。

于 2013-02-13T16:43:26.120 回答