2012-06-11 18 views
5

Esta es mi primera pregunta sobre el desbordamiento de pila, así que discúlpeme si estoy rompiendo alguna etiqueta. También soy bastante nuevo en la creación de Objective-C/app.UIManagedDocument insertar objetos en el hilo de fondo

He seguido el curso CS193P de Stanford, en particular, las clases/demostraciones de CoreData. En la aplicación Photomania de Paul Hegarty, comienza con una vista de tabla y rellena los datos en segundo plano, sin interrupción del flujo de la interfaz de usuario. He estado creando una aplicación que enumera negocios en el área local (desde una API que devuelve datos JSON).

He creado las categorías según las clases de fotógrafo/fotógrafo de Paul. La creación de las clases en sí mismas no es un problema, es el lugar donde se están creando.

A simplified data structure: 
- Section 
    - Sub-section 
     - business 
     - business 
     - business 
    - business 
    - business 
    - business 

Mi aplicación se inicia con un UIViewController con varios botones, cada uno de los cuales abre una tableview para la sección correspondiente (todos éstos funcionan bien, estoy tratando de proporcionar suficiente información para que mi pregunta tiene sentido). Llamo a un método de ayuda para crear/abrir la URL para el UIManagedDocument, que se basó en this question. Esto se invoca tan pronto como se ejecuta la aplicación y se carga rápidamente.

que tienen un método muy similar a fetchFlickrDataIntoDocument de Pablo:

-(void)refreshBusinessesInDocument:(UIManagedDocument *)document 
{ 
dispatch_queue_t refreshBusinessQ = dispatch_queue_create("Refresh Business Listing", NULL); 
dispatch_async(refreshBusinessQ, ^{ 
    // Get latest business listing 
    myFunctions *myFunctions = [[myFunctions alloc] init]; 
    NSArray *businesses = [myFunctions arrayOfBusinesses]; 

    // Run IN document's thread 
    [document.managedObjectContext performBlock:^{ 

     // Loop through new businesses and insert 
     for (NSDictionary *businessData in businesses) { 
      [Business businessWithJSONInfo:businessData inManageObjectContext:document.managedObjectContext]; 
     } 

     // Explicitly save the document. 
     [document saveToURL:document.fileURL 
      forSaveOperation:UIDocumentSaveForOverwriting 
      completionHandler:^(BOOL success){ 
       if (!success) { 
        NSLog(@"Document save failed"); 
       } 
      }]; 
     NSLog(@"Inserted Businesses"); 
    }]; 
}); 
dispatch_release(refreshBusinessQ); 
} 

[MyFunctions arrayOfBusinesses] simplemente analiza los datos JSON y devuelve un NSArray contiene businessses individuales.

He ejecutado el código con un NSLog al principio y al final del código de creación de negocio. A cada negocio se le asigna una sección, toma 0.006 segundos para crear, y hay varios cientos de estos. El inserto termina tomando alrededor de 2 segundos.

El método de ayuda está aquí:

// The following typedef has been defined in the .h file 
// typedef void (^completion_block_t)(UIManagedDocument *document); 

@implementation ManagedDocumentHelper 

+(void)openDocument:(NSString *)documentName UsingBlock:(completion_block_t)completionBlock 
{ 
    // Get URL for document -> "<Documents directory>/<documentName>" 
    NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; 
    url = [url URLByAppendingPathComponent:documentName]; 

    // Attempt retrieval of existing document 
    UIManagedDocument *doc = [managedDocumentDictionary objectForKey:documentName]; 

    // If no UIManagedDocument, create 
    if (!doc) 
    { 
     // Create with document at URL 
     doc = [[UIManagedDocument alloc] initWithFileURL:url]; 

     // Save in managedDocumentDictionary 
     [managedDocumentDictionary setObject:doc forKey:documentName]; 
    } 

    // If the document exists on disk 
    if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) 
    { 
     [doc openWithCompletionHandler:^(BOOL success) 
     { 
      // Run completion block 
      completionBlock(doc); 
     } ]; 
    } 
    else 
    { 
     // Save temporary document to documents directory 
     [doc saveToURL:url 
     forSaveOperation:UIDocumentSaveForCreating 
    completionHandler:^(BOOL success) 
     { 
      // Run compeltion block 
      completionBlock(doc); 
     }]; 
    } 
} 

y se llama en viewDidLoad:

if (!self.lgtbDatabase) { 
    [ManagedDocumentHelper openDocument:@"DefaultLGTBDatabase" UsingBlock:^(UIManagedDocument *document){ 
     [self useDocument:document]; 
    }]; 
} 

useDocument simplemente establece self.document al documento proporcionado.

Me gustaría modificar este código para que los datos se inserten en otro hilo, y el usuario todavía puede hacer clic en un botón para ver una sección, sin que la importación de datos cuelgue la interfaz de usuario.

Me agradecería cualquier ayuda He trabajado en este tema durante un par de días y no he podido resolverlo, incluso con las otras preguntas similares aquí. Si hay otra información que necesita, ¡por favor hágamelo saber!

Gracias

EDIT:

Hasta ahora esta pregunta ha recibido uno abajo voto. Si hay alguna manera de mejorar esta pregunta, o alguien sabe de una pregunta que no he podido encontrar, ¿podría comentar cómo o dónde? Si hay otra razón por la que está bajando la votación, hágamelo saber, ya que no puedo entender la negatividad, y me encantaría aprender a contribuir mejor.

Respuesta

11

Hay un par de formas de hacerlo.

dado que está utilizando UIManagedDocument usted podría tomar ventaja de NSPrivateQueueConcurrencyType para inicializar una nueva NSManagedObjectContext y utilizar performBlock hacer sus cosas. Por ejemplo:

// create a context with a private queue so access happens on a separate thread. 
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
// insert this context into the current context hierarchy 
context.parentContext = parentContext; 
// execute the block on the queue of the context 
context.performBlock:^{ 

     // do your stuff (e.g. a long import operation) 

     // save the context here 
     // with parent/child contexts saving a context push the changes out of the current context 
     NSError* error = nil; 
     [context save:&error]; 
}]; 

Al guardar desde el contexto, los datos del contexto privado se envían al contexto actual. El guardado solo está visible en la memoria, por lo que debe acceder al contexto principal (el vinculado al UIDocument) y hacer un guardar allí (consulte does-a-core-data-parent-managedobjectcontext-need-to-share-a-concurrency-type-wi).

La otra forma (mi favorita) es crear una subclase NSOperation y hacer cosas allí. Por ejemplo, declarar una subclase NSOperation como la siguiente:

//.h 
@interface MyOperation : NSOperation 

- (id)initWithDocument:(UIManagedDocument*)document; 

@end 

//.m 
@interface MyOperation() 

@property (nonatomic, weak) UIManagedDocument *document; 

@end 

- (id)initWithDocument:(UIManagedDocument*)doc; 
{ 
    if (!(self = [super init])) return nil; 

    [self setDocument:doc]; 

    return self; 
} 

- (void)main 
{ 
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init]; 
    [moc setParentContext:[[self document] managedObjectContext]]; 

    // do the long stuff here... 

    NSError *error = nil; 
    [moc save:&error]; 

    NSManagedObjectContext *mainMOC = [[self document] managedObjectContext]; 
    [mainMOC performBlock:^{ 
    NSError *error = nil; 
    [mainMOC save:&error]; 
    }]; 

    // maybe you want to notify the main thread you have finished to import data, if you post a notification remember to deal with it in the main thread... 
} 

Ahora en el hilo principal se puede establecer que la operación a una cola como la siguiente:

MyOperation *op = [[MyOperation alloc] initWithDocument:[self document]]; 
[[self someQueue] addOperation:op]; 

P. S. No puede iniciar una operación asincrónica en el método main de NSOperation. Cuando termina el main, no se invocarán los delegados vinculados con esas operaciones. Decir la verdad es posible, pero esto implica lidiar con el ciclo de ejecución o el comportamiento concurrente.

Espero que ayude.

+2

¡Eres una leyenda absoluta! He usado tu primera solución, ya que a mí me pareció más fácil de entender. Lamentablemente, no puedo darte el voto que mereces por esto, ya que acabo de unirme, pero cuando pueda lo haré. Examinaré NSOperations en una fecha posterior, ya que eso me confundió un poco. Una vez más, muchas gracias por tu ayuda! –

+0

De nada. Aclamaciones. –

+2

Solo para completar: no se olvide de informar a su UIManagedDocument que está en estado sucio después de su (y cualquier otra) operación. Así que fuerce un guardado llamando '[document updateChangeCount: UIDocumentChangeDone]'. Esto es lo que falta en el curso CS193P de Stanford. –

0

Inicialmente iba a dejar un comentario, pero creo que no tengo los privilegios para ello. Sólo quería señalar la UIDocument, más allá del cómputo cambio ofrece - (void)autosaveWithCompletionHandler:(void (^)(BOOL success))completionHandler

cual no debería tener el retraso que he experimentado con la actualización de la cuenta de cambio, ya que espera un "momento oportuno".

+0

esto no es una respuesta – johannes

Cuestiones relacionadas