Как исправить этот сбой при объединении содержимого основных данных из контекста?

У меня есть основной проект данных. Когда я делаю запрос для обновления табличного представления, он создает новый контекст в фоновом потоке, обновляет базу данных, а затем объединяет эти обновления с основным контекстом. Все работало нормально (я думаю), но теперь я получаю следующий сбой:

2013-09-13 19:01:40.873 My App[2926:a0b] -[__NSCFString controllerWillChangeContent:]: unrecognized selector sent to instance 0xc26b370
2013-09-13 19:02:00.629 My App[2926:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString controllerWillChangeContent:]: unrecognized selector sent to instance 0xc26b370'
*** First throw call stack:
(
    0   CoreFoundation                      0x027795e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x01e888b6 objc_exception_throw + 44
    2   CoreFoundation                      0x02816903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
    3   CoreFoundation                      0x0276990b ___forwarding___ + 1019
    4   CoreFoundation                      0x027694ee _CF_forwarding_prep_0 + 14
    5   CoreData                            0x002217b0 -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 2080
    6   Foundation                          0x015dbe39 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40
    7   CoreFoundation                      0x027d5524 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
    8   CoreFoundation                      0x0272d07b _CFXNotificationPost + 2859
    9   Foundation                          0x01515b91 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98
    10  CoreData                            0x001264a3 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83
    11  CoreData                            0x0013be96 -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] + 3734
    12  CoreData                            0x0013afed -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] + 429
    13  My App                            0x0000bc45 -[MAManager updateMainContext:] + 245
    14  libobjc.A.dylib                     0x01e9a81f -[NSObject performSelector:withObject:] + 70
    15  Foundation                          0x0155dc18 __NSThreadPerformPerform + 285
    16  CoreFoundation                      0x027028af __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    17  CoreFoundation                      0x0270223b __CFRunLoopDoSources0 + 235
    18  CoreFoundation                      0x0271f30e __CFRunLoopRun + 910
    19  CoreFoundation                      0x0271eb33 CFRunLoopRunSpecific + 467
    20  CoreFoundation                      0x0271e94b CFRunLoopRunInMode + 123
    21  GraphicsServices                    0x0348e9d7 GSEventRunModal + 192
    22  GraphicsServices                    0x0348e7fe GSEventRun + 104
    23  UIKit                               0x0067b94b UIApplicationMain + 1225
    24  My App                            0x00003442 main + 146
    25  libdyld.dylib                       0x0236d725 start + 0
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Вот соответствующий фрагмент кода, на котором он ломается:

 // merge changes to main context,fetchedRequestController will automatically monitor the changes and update tableview.
- (void)updateMainContext:(NSNotification *)notification {
    assert([NSThread isMainThread]);
    NSLog(@"Merging changes from context.");
    [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
    [self save]; // update fetched results controllers
}

// this is called via observing "NSManagedObjectContextDidSaveNotification" from the background thread data load
- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification {
    NSLog(@"Context saved!");
    if (notification.object != [self managedObjectContext]) {
        NSLog(@"Background context propagating to main context.");
        [self performSelectorOnMainThread:@selector(updateMainContext:) withObject:notification waitUntilDone:NO];
    }
}

Кто-нибудь знает, почему я получаю эту ошибку и как ее исправить?


person Gujamin    schedule 13.09.2013    source источник


Ответы (3)


Некоторый объект, зарегистрированный для уведомления, выглядит так, как будто он был освобожден, а его адрес повторно используется для хранения строки вместо того, что ожидалось.

Включите зомби для своей схемы, запустите ее снова и выясните, какому объекту отправляется уведомление. Затем убедитесь, что вы сохраняете объект живым, пока он вам нужен.

person Phillip Mills    schedule 13.09.2013

Ваш сбой - это нераспознанный селектор с методом [__NSCFString controllerWillChangeContent:]. Этот метод вызывается для делегата FRC, но каким-то образом вы выполняете обратный вызов для NSString, который не реализует этот метод. Можете ли вы опубликовать код, в котором вы настроили свой NSFetchedResultsController в этом viewController?

person Patrick Goley    schedule 13.09.2013

Похоже, это была проблема с несколькими одновременными запросами на сохранение (из разных контекстов управляемых объектов, но, тем не менее, из разных потоков). Решение состояло в том, чтобы обернуть операции сохранения в блок @synchronized, чтобы гарантировать, что операции сохранения были атомарными и не происходили одновременно:

- (void) save {
    // lock and wait if another save operation is in progress
    @synchronized([self class]) {
        NSError *error;
        if (![self save:&error]) {
            NSLog(@"Whoops, couldn't save: %@", error);
        }
    }
}
person Gujamin    schedule 15.09.2013