objective-c ios core-data nsfetchedresultscontroller nsmanagedobjectcontext

objective c - Datos principales anidados en contextos de objetos gestionados y bloqueos frecuentes/congelaciones



objective-c ios (7)

Tengo un problema que es casi idéntico al problema descrito por esta persona aquí, pero no ha recibido respuesta:

http://www.cocoabuilder.com/archive/cocoa/312683-core-data-nested-managed-object-contexts-and-frequent-deadlocks.html#312683

Aquí está el problema:

Tengo una configuración de MOC padre con NSPrivateQueueConcurrencyType y un conjunto de coordinador de tienda persistente, tiene una configuración MOC secundaria con NSMainQueueConcurrencyType. La idea de que la mayor parte del trabajo arduo y largo se ahorre se puede hacer en el MOC privado, lo que libera al hilo principal del bloqueo de la IU. Desafortunadamente, parece que me encuentro con un par de situaciones que causan bloqueos.

Si el MOC secundario (en el hilo principal) realiza una búsqueda con NSFetchedResultsController, el contexto principal se envía a -executeFetchRequest: puede crear un interbloqueo. Ambas operaciones se realizan dentro del contexto de un performBlock: para sus respectivos MOC aunque los documentos parecen indicar que el uso de un tipo de concurrencia de subproceso principal MOC en el subproceso principal sin performBlock: está bien.

Parece que la cola privada está esperando en el bloqueo de PSCs que el contexto secundario en el hilo principal ya ha bloqueado. Parece que el contexto secundario (mientras mantiene el bloqueo de los PSC) está intentando enviar el sincronizador al contexto primario y, por lo tanto, ambos están esperando el uno al otro.

¿PriveQueue -> MainQueue es una configuración compatible? Parece que la mayoría de las personas todavía tienen el contexto principal en el hilo principal.

El hilo principal se ve así:

> #0 0x960f6c5e in semaphore_wait_trap () > #1 0x04956bb5 in _dispatch_thread_semaphore_wait () > #2 0x04955c8f in _dispatch_barrier_sync_f_slow () > #3 0x04955dea in dispatch_barrier_sync_f () > #4 0x01797de5 in _perform () > #5 0x01798547 in -[NSManagedObjectContext(_NestedContextSupport) newValuesForObjectWithID:withContext:error:] () > #6 0x0176416b in _PFFaultHandlerLookupRow () > #7 0x01763f97 in -[NSFaultHandler fulfillFault:withContext:forIndex:] () > #8 0x01763b75 in _PF_FulfillDeferredFault () > #9 0x017639f2 in _sharedIMPL_pvfk_core () > #10 0x017681a0 in _pvfk_11 () > #11 0x0001b322 in -[FBUser sectionName] at /Users/mlink/Code/x/x/FBUser.m:62 > #12 0x011a8813 in _NSGetUsingKeyValueGetter () > #13 0x017a0652 in -[NSManagedObject valueForKey:] () > #14 0x011ab8d5 in -[NSObject(NSKeyValueCoding) valueForKeyPath:] () > #15 0x01851f72 in -[NSFetchedResultsController(PrivateMethods) _sectionNameForObject:] () > #16 0x01853af6 in -[NSFetchedResultsController(PrivateMethods) _computeSectionInfo:error:] () > #17 0x01850ea6 in -[NSFetchedResultsController performFetch:] () > #18 0x0003a4fc in __62-[SYFriendsTableViewController updateFetchedResultsController]_block_invoke_0 () > #19 0x01797af3 in developerSubmittedBlockToNSManagedObjectContextPerform () > #20 0x049554f0 in _dispatch_main_queue_callback_4CF () > #21 0x01b3e833 in __CFRunLoopRun () > #22 0x01b3ddb4 in CFRunLoopRunSpecific () > #23 0x01b3dccb in CFRunLoopRunInMode () > #24 0x023d6879 in GSEventRunModal () > #25 0x023d693e in GSEventRun () > #26 0x0089aa9b in UIApplicationMain () > #27 0x00002656 in main at /Users/mlink/Code/x/x/main.mm:16

la pila de cola privada se ve así:

#0 0x960f8876 in __psynch_mutexwait () #1 0x97e9e6af in pthread_mutex_lock () #2 0x0172ec22 in -[_PFLock lock] () #3 0x0172ebfa in -[NSPersistentStoreCoordinator lock] () #4 0x01746a8c in -[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore] () #5 0x01745030 in -[NSManagedObjectContext executeFetchRequest:error:] () #6 0x0009d49f in -[NSManagedObjectContext(Additions) executeFetchRequest:] at /Users/mlink/Code/objc/C/C/NSManagedObjectContext+Additions.m:44 #7 0x0002177f in +[FBUser usersForFbids:inManagedObjectContext:] at /Users/mlink/Code/x/x/FBUser.m:435 #8 0x00021fc0 in __77+[FBUser updateUserFromGraphValues:inManagedObjectContext:completionHandler:]_block_invoke_0 at /Users/mlink/Code/x/x/FBUser.m:461 #9 0x0180f9f3 in developerSubmittedBlockToNSManagedObjectContextPerform_privateasync () #10 0x04954ecf in _dispatch_queue_drain () #11 0x04954d28 in _dispatch_queue_invoke () #12 0x049544af in _dispatch_worker_thread2 () #13 0x97ea1b24 in _pthread_wqthread () #14 0x97ea36fe in start_wqthread ()

Él también escribe esto:

Estoy empezando a pensar que el problema es con NSFetchedResultsController, que siempre está bloqueado en performFetch: cuando se producen estos bloqueos. La mayoría de las veces estará atascado intentando fallar en un objeto como resultado de pedir su nombre de sección. Como prueba, traté de reproducir lo que hace el FRC y realicé el executeFetchRequest: y luego repetí los resultados preguntando a cada objeto por su nombre de sección. Y esto no causa un punto muerto. Si dejo el FRC para realizar la función Obtener: después de hacer mi prueba, todavía se estancará allí. Estoy 99% seguro de que el FRC tiene un problema de sincronización con contextos anidados.

Pregunta: ¿Alguien sabe por qué ocurre este problema? sabes como resolverlo? ¿Es esto un error?


La idea de que la mayor parte del trabajo largo y duro y ahorra se puede hacer en el MOC privado

¿Cómo implementa esa idea? ¿Usas algo como esto?

- (void)doSomethingWithDocument:(UIManagedDocument *)document { NSManagedObjectContext *parent = document.managedObjectContext.parentContext; [parent performBlock:^{ /* Long and expensive tasks.. execute fetch request on parent context download from remote server */ // save document }]; }

Lo hice arriba y me atascaron también. Luego traté de no tocar la cola de respaldo del contexto principal. En cambio, uso GCD simple y simple para hacer las descargas y manipular los datos centrales en el contexto del niño (en la cola principal). Funciona bien. De esta manera, el contexto de los padres parece inútil ... Pero al menos no causa un punto muerto.

- (void)doSomethingWithDocument:(UIManagedDocument *)document { dispatch_queue_t fetchQ = dispatch_queue_create("Flickr fetcher", NULL); dispatch_async(fetchQ, ^{ // download from remote server // perform in the NSMOC''s safe thread (main thread) [document.managedObjectContext performBlock:^{ // execute fetch request on parent context // save document }]; }); dispatch_release(fetchQ); }


Acabo de leer esta publicación SO, donde fabrice truillot de chambrier recomienda no utilizar contextos anidados en la actualidad. Él hace una referencia al artículo Core Data Growing Dolores .

De ese artículo:

NSFetchedResultsController deadlocks

Nunca desea que su aplicación se estanque. Con NSFetchedResultsController y contextos anidados, es bastante fácil de hacer. Utilizando la misma configuración de UIManagedDocument descrita anteriormente, la ejecución de solicitudes de recuperación en el contexto de cola privada mientras se usa NSFetchedResultsController con el contexto de la cola principal probablemente sea un punto muerto. Si comienzas ambos al mismo tiempo, sucede con casi un 100% de consistencia. NSFetchedResultsController probablemente está adquiriendo un bloqueo que no debería ser. Se informó que esto se corrigió en un próximo lanzamiento de iOS.

radar: // 11861499 corregido en una próxima versión

Esto parece describir exactamente su problema.


Me sucede porque los padres están configurados con NSMainQueueConcurencyType

Para resolver eso, hago que el contexto de managedobject para mainQueue sea un elemento secundario. Llamé a restablecer cada vez que quiero cargar cosas para garantizar que los datos en mainQueue sean los mismos con los padres. A menudo no lo es.


Para mi aplicación iOS 6, tengo la misma configuración de concurrencia que el OP: un MOC padre que usa una cola privada y un MOC secundario en el hilo principal. También tengo un NSFetchedResultsController que usa el MOC hijo para actualizar un UITableViewController. Ambos MOC se inicializan en AppDelegate y se utilizarán en toda la aplicación. AppDelegate tiene dos métodos, savePrivateThreadMOCToCoreData y saveMainThreadMOCToCoreData , para conservar los cambios en el CD. En el lanzamiento, distribuyo un inicializador de coredatos en una cola privada de la siguiente manera. La idea es colocar inmediatamente al usuario en la vista de tabla y permitir que el inicializador actualice los datos del núcleo en segundo plano.

dispatch_async(private_queue,^{ [CoreDataInitializer initialize]; });

Inicialmente, cuando savePrivateThreadMOCToCoreData estaba haciendo save en a -performBlock, estaba viendo los mismos deadlocks de psynch_mutex descritos en " Core Data Growing Dolores " vinculado anteriormente. También vi bloqueos si intenté leer datos en TableVC mientras estaba en curso el guardado.

Collection <__NSCFSet: 0x7d8ea90> was mutated while being enumerated.

Para superar esto, cambié a hacer guardias usando -performBlockAndWait. Dejé de ver bloqueos y bloqueos, pero no me pareció bien hacer que la IU aguardara a guardar. Finalmente, eliminé todas las llamadas a -performBlock * y usé un simple vainilla [privateMOC save: & error] y así desaparecieron todos mis problemas. El controlador de resultados obtenido lee los datos parcialmente guardados limpiamente y actualiza la tabla, no hay más bloqueos o errores "mutados mientras se enumeran".

Sospecho que se supone que -performBlock * debe ser utilizado por otros hilos, que no crearon el MOC en cuestión, para solicitar operaciones en él. Dado que tanto mi MOC de hilo principal como el privado pertenecen al delegado de la aplicación, los salvados en el MOC privado no deberían usar -performBlock *.

Probablemente sea relevante que aunque mi entorno de compilación es iOS 6, mi objetivo de implementación base es SDK iOS 5.0. Parece que otros ya no están viendo este problema con iOS 6.


Resolví exactamente el mismo problema con los bloqueos ocasionados por la obtención simultánea de dos hilos (BG ejecutó fetchRequest disparado, MAIN uno realizó la búsqueda de NSFRC). La solución es crear un nuevo contexto para la operación de sincronización de larga ejecución. No tiene contexto padre, tiene tipo de concurrencia NSPrivateQueueConcurrencyType y está directamente relacionado con un PSC común. Después de que todo el trabajo de larga ejecución se realiza dentro de este contexto en segundo plano, lo mergeChangesFromContextDidSaveNotification y lo mergeChangesFromContextDidSaveNotification con la pila de contextos paralelos de reposo mediante la rutina mergeChangesFromContextDidSaveNotification .

Una gran solución se implementa en Magical Record 3. Vea más información aquí: https://.com/a/25060126/1885326 .


Solo quería entrar y aceptar completamente evitar los contextos anidados. He estado trabajando en iOS 7 con contextos anidados (cola principal hija y padre de cola privada) y NSFetchedResultsControllers y tuve un problema de interbloqueo imposible de arreglar. Cambié a usar MOC independientes y guardar notificaciones y el problema desapareció.

Si alguien necesita una guía rápida sobre cómo cambiar su código, esta página tiene un código listo (simplemente ignore la recomendación de contexto anidado):

http://www.cocoanetics.com/2012/07/multi-context-coredata/


Yo también tengo un bloqueo relacionado con developerSubmittedBlockToNSManagedObjectContextPerform .

En mi caso, considere el siguiente patrón de llamada al método:

[privatecontext performBlock:^{ A(CDManager.privatecontext); }];

donde: A (CDManager.privateContext) llama B () B () llama C () C () llama D ()

y: el método A () y el método C () contienen algunas operaciones de Datos básicos. A () ya tiene el conocimiento sobre qué contexto trabajar, pero A () no informa B () sobre el contexto y, por lo tanto, C () tampoco tiene información sobre el contexto en el que trabajar, por lo que C () funciona en contexto predeterminado (principal). y esto causa el bloqueo debido a datos de forma inconsistente en db.

solución: todos los métodos que deben funcionar en las operaciones de base de datos se parametrizan con el contexto en el que van a trabajar, excepto D () ya que no es necesario trabajar en la operación de base de datos, como por ejemplo:

A (contexto) llamadas B (contexto) B (contexto) llamadas C (contexto) C (contexto) llamadas D ()