ios - Los objetos de datos centrales se vuelven nulos
iphone objective-c (5)
El problema es que (como escribí en un comentario) que los objetos se captan en un hilo de fondo, pero se usan en el hilo principal (UI). Los objetos gestionados solo pueden "vivir" en el contexto en el que fueron creados. Si el contexto está desasignado, los objetos aún existen, pero los métodos de acceso a la propiedad devuelven nil
.
Soluciones posibles:
- Obtener los objetos en el hilo principal.
Utilizar
NSManagedObject *copy = [[mainContext objectWithID:[object objectID]];
para "copiar" los objetos del contexto de fondo al contexto principal. (Quizás MagicalRecord tiene un método de conveniencia).
En lugar de buscar objetos gestionados, establecer
[fetchRequest setResultType:NSDictionaryResultType]; [fetchRequest setPropertiesToFetch:@[@"title", ...]];
para buscar una matriz de diccionarios con los atributos que le interesan
Estoy creando una aplicación de tareas que debería soportar el modo fuera de línea. Utilicé RestKit para descargar tareas y mapeé eso en datos Core locales.
Esto funciona bien en el modo en línea. Pero en línea hay un problema extraño. Uso NSPredicate para recuperar datos del almacenamiento local. Para esto estoy usando Magical Records.
+ (void)getIdeasTasksWithPageNo:(int)pageNo completionHandler:(void (^)(NSArray *, NSError *))completionHandler {
NSArray *tasks = [self MR_findAllWithPredicate:[NSPredicate predicateWithFormat:@"due_date = nil AND user_id = %@", [DBUsers currentUser].id]];
completionHandler(tasks, nil);
}
Y lo llamo así:
[DBTasks getIdeasTasksWithPageNo:1 completionHandler:^(NSArray *tasks, NSError *error) {
if (!error) {
[self displayTasksWithResults:tasks forPageNo:1];
} else {
NSLog(@"Error is %@", error);
}
}];
Y así es como lo estoy mostrando en UITableView
-(void)displayTasksWithResults:(NSArray *)tasks forPageNo:(int)pageNo {
if (!self.tasksArray) {
self.tasksArray = [[NSMutableArray alloc] init];
} else {
[self.tasksArray removeAllObjects];
}
[self.tasksArray addObjectsFromArray:tasks];
[self.tableview reloadData];
}
Esto funciona solo por primera vez y todas las tareas se llenan en UITableView
.
El problema es que después de que UITableView
se UITableView
, todos los registros en self.tasksArray
vuelven Null
. Si UITableView
, las filas de la tabla comienzan a estar vacías.
Pero si self.tasksArray
en el método displayTasksWithResults
, se imprime perfectamente.
(
"Title: Task 01",
"Title: You''ve gone incognito. Pages you view in incognito tabs won''t stick around in your browser''s history, cookie store, or search history after you''ve closed all of your incognito tabs. Any files you download or bookmarks you create will be kept. ",
"Title: Task 06",
"Title: Task 04",
"Title: Hi",
"Title: Task 3",
"Title: Task 4",
"Title: Hi 4",
"Title: hh",
"Title: Task 02",
"Title: Task 05/n",
"Title: Task 4",
"Title: Task 5",
"Title: Task 2 updated",
"Title: Here is a task. ",
"Title: Task 03",
"Title: Hi 3",
"Title: Task 2",
"Title: Hi 2",
"Title: Testing task email with Idea Task",
"Title: Task f6",
"Title: 1.117",
"Title: Task f5",
"Title: Task f12",
"Title: Task f4",
"Title: Task f3",
"Title: 111.0.113",
"Title: 111.0.115",
"Title: Pages you view in incognito tabs won''t stick around in your browser''s history, cookie store, or search history after you''ve closed all of your incognito tabs. Any files you download or bookmarks you create will be kept.",
"Title: Task f7",
"Title: 1.116",
"Title: 1.118",
"Title: Going incognito doesn''t hide your browsing from your employer, your internet service provider, or the websites you visit. ",
"Title: 111.0.111"
)
Si self.taskArray
más tarde, puede estar en el delegado UITableView
de UITableView
, se imprime como a continuación:
(
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)"
)
Creo que esto puede estar relacionado con NSManagedObjectContext
, pero no sé cómo solucionarlo.
¡Por favor ayuda!
En primer lugar, compruebe si está utilizando CoreData en una circunstancia de hilos múltiples, si lo hace, puede que necesite agregar algún código de cola de envío para asegurarse de que su código CoreData se ejecute en un solo hilo. En segundo lugar, llame a otros métodos de su arreglo de resultados para ver si el NSManagedObject está en estado de falla, llamando a algunos métodos hará que se obtenga.
Finalmente encontré la razón para objetos nulos. Estaba llamando a buscar en el fondo. Así que Magical Records crea un nuevo contexto de objetos gestionados específico para ese hilo, en NSManagedObject+MagicalFinders.m
+ (NSManagedObjectContext *) MR_contextForCurrentThread;
{
if ([NSThread isMainThread])
{
return [self MR_defaultContext];
}
else
{
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey];
if (threadContext == nil)
{
threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
[threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey];
}
return threadContext;
}
}
Así que necesitaba copiar objetos del contexto del hilo de fondo al contexto del hilo principal. Finalmente encontré la solución aquí y creé mi método así.
+ (void)backgroundFetchWithPredicate:(NSPredicate *)predicate completion:(void(^)(NSArray *, NSError *))completion {
NSManagedObjectContext *privateContext = [NSManagedObjectContext MR_context];
[privateContext performBlock:^{
NSArray *privateObjects = [self MR_findAllWithPredicate:predicate inContext:privateContext];
NSArray *privateObjectIDs = [privateObjects valueForKey:@"objectID"];
// Return to our main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSPredicate *mainPredicate = [NSPredicate predicateWithFormat:@"self IN %@", privateObjectIDs];
NSArray *finalResults = [self MR_findAllWithPredicate:mainPredicate];
completion(finalResults, nil);
});
}];
}
Ahora solo necesito llamarlo así:
[self backgroundFetchWithPredicate:predicate completion:^(NSArray *results, NSError *error) {
completionHandler(results, error);
}];
He encontrado varios puntos débiles en tu código, intenta resolverlos:
Nunca se usa el parámetro
pageNo
en el+ (void)getIdeasTasksWithPageNo:(int)pageNo completionHandler:(void (^)(NSArray *, NSError *))completionHandler
Siempre pasa nil para el parámetro de
error
en el método anterior¿Cuál es el propósito de eliminar objetos de la matriz
[self.tasksArray removeAllObjects];
? (primero intente comentar esta línea tal vez esta es la razón)
Supongo que es porque estás asignando directamente el valor a la matriz. Intente asignar valor usando mutablecopy
NSArray *tasks = [[self MR_findAllWithPredicate:[NSPredicate predicateWithFormat:@"due_date = nil AND user_id = %@", [DBUsers currentUser].id]] mutableCopy];