Исключение / Сбой при объединении контекстов, управляемых CoreData - PullRequest
3 голосов
/ 11 февраля 2012

Я получаю следующее исключение при попытке объединить управляемый контекст (запущенный в фоновом потоке) с моим основным управляемым контекстом (в mainthread). Кажется, я не могу уловить исключение в своем собственном выражении @try. У кого-нибудь есть понимание этого вопроса?

Я использую политику слияния по умолчанию, но я не уверен, что это правильно - эта проблема очень прерывистая - случается редко, но вызывает сбой моего приложения.

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread:  0

Last Exception Backtrace:
0   CoreFoundation                  0x37e3b8bf __exceptionPreprocess + 163
1   libobjc.A.dylib                 0x319211e5 objc_exception_throw + 33
2   CoreData                        0x344b7ea5 -[NSSQLiteStatement cachedSQLiteStatement] + 1
3   CoreData                        0x344b774f -[NSSQLiteConnection prepareSQLStatement:] + 55
4   CoreData                        0x3455b049 -[NSSQLChannel selectRowsWithCachedStatement:] +  61
5   CoreData                        0x34586d63 newFetchedRowsForFetchPlan_MT + 783
6   CoreData                        0x344bfb07 -[NSSQLCore newRowsForFetchPlan:] + 351
7   CoreData                        0x34565011 -[NSSQLCore fetchRowForObjectID:] + 1005
8   CoreData                        0x344d1a57 -[NSSQLCore newValuesForObjectWithID:withContext:error:] + 195
9   CoreData                        0x344d0f83 _PFFaultHandlerLookupRow + 423
10  CoreData                        0x3450e111 -[NSFaultHandler fulfillFault:withContext:] + 25
11  CoreData                        0x34518999 -[NSManagedObject(_NSInternalMethods) _newPropertiesForRetainedTypes:andCopiedTypes:preserveFaults:] + 77
12  CoreData                        0x345178ef -[NSManagedObject(_NSInternalMethods) _newAllPropertiesWithRelationshipFaultsIntact__] + 79
13  CoreData                        0x345284db -[NSManagedObjectContext(_NSInternalChangeProcessing) _establishEventSnapshotsForObject:] + 47
14  CoreData                        0x3452694b -[NSManagedObjectContext deleteObject:] + 155
15  CoreData                        0x345238a1 -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] + 813
16  CoreData                        0x34522c35 -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] + 189
17  myapp                       0x0008f0e9 0x8d000 + 8425
18  CoreFoundation                  0x37d9a22b -[NSObject performSelector:withObject:] + 43
19  Foundation                      0x31d75757 __NSThreadPerformPerform + 351
20  CoreFoundation                  0x37e0fb03 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
21  CoreFoundation                  0x37e0f2cf __CFRunLoopDoSources0 + 215
22  CoreFoundation                  0x37e0e075 __CFRunLoopRun + 653
23  CoreFoundation                  0x37d914dd CFRunLoopRunSpecific + 301
24  CoreFoundation                  0x37d913a5 CFRunLoopRunInMode + 105
25  GraphicsServices                0x3790ffcd GSEventRunModal + 157
26  UIKit                           0x35221743 UIApplicationMain + 1091

Я запускаю фоновый контекст в start () nsoperation следующим образом:

AppDelegate *appController = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[appController setPersistentStore:_managedObjectContext];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedDeletedObjects:) name:NSManagedObjectContextDidSaveNotification object:_managedObjectContext];

Я также установил событие уведомления, которое вызывается при удалении объектов в фоновом управляемом контексте, затем выполняется обратный вызов:

-(void)receivedDeletedObjects:(NSNotification *)note
{
    AppDelegate *appController = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [self managedObjectContext];
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:note waitUntilDone:NO];

}

Это в значительной степени код. У меня есть 4 различных фоновых потока, каждый из которых имеет свой собственный управляемый контекст, выполняя одно и то же, сливаясь с основным контекстом таким образом. Мне интересно, что несколько потоков попадают в mergeChangesFromContextDidSaveNotification одновременно, но это не должно иметь место, так как он всегда вызывается в mainthread.

1 Ответ

0 голосов
/ 31 октября 2012

Как насчет этого:

(все в AppDelegate, вызовите freshContextForBackgroundTask из фонового потока и используйте save: для запуска слияния.) Примечание. SyncObj - это простой экземпляр NSObject, необходимый для синхронизации внутри делегата приложения при использовании многих фоновых потоков (или просто неудачи с планирования)

-(NSManagedObjectContext*) freshContextForBackgroundTask {
    @synchronized(syncObj) {
        NSManagedObjectContext* r = [[NSManagedObjectContext alloc] init];
        [r setPersistentStoreCoordinator:self.managedObjectContext.persistentStoreCoordinator];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(backgroundContextDidSave:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:r];
        return r;
    }
}


- (void)backgroundContextDidSave:(NSNotification *)notification {
    /* Make sure we're on the main thread when updating the main context */
    //NSLog(@"merging change: %@",notification);
    dispatch_async(dispatch_get_main_queue(), ^{
        NSManagedObjectContext *context = [self managedObjectContext];
        // this for loop may not be needed for your purpose.
        for(NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey]) {
            [[context objectWithID:[object objectID]] willAccessValueForKey:nil];
        }
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];

    });
}

Ваш подход кажется странным. Особенно, когда вы устанавливаете постоянное хранилище делегата приложения в (nil) один из только что инициированных контекстов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...