iOS: el hilo no vuelve al hilo principal

Tengo problemas con mis hilos. Después de cambiar un par de veces entre 2 pantallas cuando el hilo está ocupado. El subproceso no realiza todas las líneas ... El punto de interrupción simplemente desaparece cuando tiene que volver al subproceso principal. ¿Puede alguien por favor ayudarme?

Suelto el hilo cuando la vista se descarga.

Gracias,

- (void)fetchFeedDataIntoDocument
{
    NSString * labelString = [NSString stringWithFormat:@"Feed Fetcher %@", self.pageTitle];
    const char *label = [labelString UTF8String];

    self.fetchF = dispatch_queue_create(label, NULL);
    dispatch_async(self.fetchF, ^{

        NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:@"up"];

        NSDictionary *lastfeed;

        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

        NSManagedObjectContext *context = [appDelegate getManagedObjectContext];

        if ([feeds count] > 0)
        {
            lastfeed = [feeds objectAtIndex:0];

            [FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:@"id"] AndPulishDate:[lastfeed objectForKey:@"publish_up"]];
        }

        for (NSDictionary *feedInfo in feeds) {
            [Feed FeedWithInfo:feedInfo InManageObject:context];
        }

        NSError *error = nil;

        [context save:&error];

        if (error){
            NSLog(@"Error save : %@", error);}

        dispatch_async(dispatch_get_main_queue(), ^{
            [self setupFetchedResultsController];
            [self.tableView reloadData];
            [self downloadImagesForFeeds:feeds];
        });

    });

preguntado el 03 de mayo de 12 a las 13:05

4 Respuestas

Está accediendo al ManagedObjectContext desde un subproceso diferente de donde se creó. Esta es la regla de subprocesamiento de datos básicos n.º 1.

Obtiene el MOC del delegado de la aplicación. Si es el MOC normal generado por Xcode, se crea con simultaneidad de confinamiento de subprocesos. Ni siquiera puedes llamar a performBlock con él. Solo puede acceder a ese MOC desde el hilo principal. Período. Cualquier otra cosa es jugar con fuego, en el mejor de los casos.

Si desea hacer todo el trabajo en un hilo separado, también necesita un MOC separado. Así (recién escrito - no compilado)...

NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = appDelegate.managedObjectContext;
[moc performBlock:^{
    // Go get your remote data and whatever you want to do

    // Calling save on this MOC will push the data up into the "main" MOC
    // (though it is now in the main MOC it has not been saved to the store).
    [moc save:&error];
}];

Lo que se traduciría en algo como esto...

- (void)fetchFeedDataIntoDocument
{
    NSString * labelString = [NSString stringWithFormat:@"Feed Fetcher %@", self.pageTitle];
    const char *label = [labelString UTF8String];

    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *mainContext = [appDelegate getManagedObjectContext];
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    context.parentContext = mainContext;
    [context performBlock:^{    
        NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:@"up"];

        NSDictionary *lastfeed;


        if ([feeds count] > 0)
        {
            lastfeed = [feeds objectAtIndex:0];

            [FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:@"id"] AndPulishDate:[lastfeed objectForKey:@"publish_up"]];
        }

        for (NSDictionary *feedInfo in feeds) {
            [Feed FeedWithInfo:feedInfo InManageObject:context];
        }

        NSError *error = nil;

        [context save:&error];

        if (error){
            NSLog(@"Error save : %@", error);}
DO you really want to continue on error?
        dispatch_async(dispatch_get_main_queue(), ^{
            // Data has been pushed into main context from the background
            // but it still needs to be saved to store...
            // Do not forget to perform error handling...
            NSError *error = nil;
            [mainContext save:&error];
            [self setupFetchedResultsController];
            [self.tableView reloadData];
            [self downloadImagesForFeeds:feeds];
        });

    });

EDITAR

El código generado por Xcode para crear el MOC usa init, que usa NSConfinementConcurrencyType. Puede reemplazarlo con MainConcurrency, sin ningún problema, pero obtiene varios beneficios.

En el archivo de delegado de su aplicación, reemplace...

    __managedObjectContext = [[NSManagedObjectContext alloc] init];

con este...

    __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

Ahora, su MOC principal puede ser "emparentado" y también puede llamar a performBlock en él.

contestado el 03 de mayo de 12 a las 14:05

Ahora recibo este error :), Terminando la aplicación debido a la excepción no detectada 'NSInvalidArgumentException', motivo: 'El NSManagedObjectContext principal debe usar NSPrivateQueueConcurrencyType o NSMainQueueConcurrencyType'. - danny boevee

Bien, eso significa que está utilizando el MOC "predeterminado" creado por la plantilla de Xcode. Agregaré un fragmento de código a la respuesta anterior en un minuto. - jody hagins

Muchas gracias, creo que está resuelto: D, está funcionando bien ahora - danny boevee

Gracias, esto me recordó establecer el contexto principal. Por cierto, para otros que lean esto, no usen la configuración MOC "predeterminada". Está ahí como una plantilla de orientación. Recomiendo encarecidamente crear su propia pila. Siempre estaba creando el mío propio y ahora acabo de crear un envoltorio muy simple. - Arvin

@JodyHagins No quiero cambiar el contexto del objeto administrado de mi Appdelegate. ¿Cómo puedo crear de forma segura un contexto secundario? - nr5

que tal hacer esto...

-(void)functionToCallFetch {

     [self performSelectorInBackground:@selector(fetchFeedDataIntoDocument) withObject:nil];

} 

- (void)fetchFeedDataIntoDocument
{

     NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:@"up"];

        NSDictionary *lastfeed;

        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

        NSManagedObjectContext *context = [appDelegate getManagedObjectContext];

        if ([feeds count] > 0)
        {
            lastfeed = [feeds objectAtIndex:0];

            [FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:@"id"] AndPulishDate:[lastfeed objectForKey:@"publish_up"]];
        }

        for (NSDictionary *feedInfo in feeds) {
            [Feed FeedWithInfo:feedInfo InManageObject:context];
        }

        NSError *error = nil;

        [context save:&error];

        if (error){
            NSLog(@"Error save : %@", error);}

        //dispatch_async(dispatch_get_main_queue(), ^{
        //    [self setupFetchedResultsController];
        //    [self.tableView reloadData];
        //    [self downloadImagesForFeeds:feeds];
        //});
        [self performSelectorOnMainThread:@selector(setupFetchedResultsController) withObject:nil waitUntilDone:NO];
        [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
        [self performSelectorOnMainThread:@selector(downloadImagesForFeeds:) withObject:feeds waitUntilDone:NO];


}

¿Quizás eso funcionaría mejor?

contestado el 03 de mayo de 12 a las 13:05

Mmm... Sigue siendo lo mismo :(. el punto de quiebre simplemente desaparece cuando hay que realizar los selectores - danny boevee

Cambiar el siguiente código..

   dispatch_queue_t queue1 = dispatch_queue_create("com.MyApp.AppTask",NULL);
   dispatch_queue_t main = dispatch_get_main_queue();
   dispatch_async(queue1, 
               ^{
                   dispatch_async(main, 
                                  ^{
                                      [self setupFetchedResultsController];
                                      [self.tableView reloadData];
                                      [self downloadImagesForFeeds:feeds];

                                  });

               });  

Espero que esto te ayudará

contestado el 03 de mayo de 12 a las 13:05

Lo siento en este momento, mi safari no muestra el bloque de código, la opción de URL. Así que no agregué el bloque de código. Editaré después de ver la opción de bloque de código .. - Nitin

¿Intentaste construir un método como:

- (void)methodToBePerformedOnMainThread{
        [self setupFetchedResultsController];
        [self.tableView reloadData];
        [self downloadImagesForFeeds:feeds];
}

y llámalo con

[self performSelectorOnMainThread:@selector(methodToBePerformedOnMainThread) withObject:nil waitUntilDone:NO];

al final de fetchFeedDataIntoDocument

Editar:

¿Intentó envolverlo con NSOperationQueue en lugar de dispatch_queue?

Como :

NSOperationQueue *operationQueue = [NSOperationQueue new];

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(fetchFeedDataIntoDocument) object:nil];

if(operation != nil){
    [operationQueue addOperation:operation];

contestado el 03 de mayo de 12 a las 14:05

Siempre lo mismo. No ayudó :( - danny boevee

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.