2012-06-11 11 views
5

Questa è la mia prima domanda su Stack Overflow, quindi per favore scusami se interrompo qualsiasi etichetta. Sono anche abbastanza nuovo per la creazione di Objective-C/app.UIManagedDocument inserisce oggetti nel thread di sfondo

Ho seguito il corso di Stanford CS193P, in particolare, le conferenze/demo CoreData. Nell'app Photomania di Paul Hegarty, inizia con una vista tabella e inserisce i dati in background, senza alcuna interruzione nel flusso dell'interfaccia utente. Ho creato un'applicazione che elenca le attività commerciali nell'area locale (da un'API che restituisce i dati JSON).

Ho creato le categorie come da foto per fotografi/fotografi di Paul. La creazione delle classi stesse non è un problema, è dove vengono creati.

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

La mia applicazione si avvia con un UIViewController con diversi pulsanti, ognuno dei quali si apre un Tableview per la sezione corrispondente (questi tutti funzionano bene, sto cercando di fornire abbastanza informazioni in modo che la mia domanda ha un senso). Chiamo un metodo di supporto per creare/aprire l'URL per UIManagedDocument, che era basato su this question. Questo viene chiamato non appena l'applicazione viene eseguita e si carica rapidamente.

Ho un metodo molto simile a fetchFlickrDataIntoDocument di Paolo:

-(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] solo analizza i dati JSON e restituisce un NSArray contenente singoli businessses.

Ho eseguito il codice con un NSLog all'inizio e alla fine del codice di creazione dell'attività. Ogni azienda viene assegnata una sezione, impiega 0,006 secondi per creare e ce ne sono diverse centinaia. L'inserto ha una durata di circa 2 secondi.

Il Metodo Helper è qui:

// 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); 
     }]; 
    } 
} 

e si chiama in viewDidLoad:

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

useDocument imposta solo self.document la documentazione fornita.

Desidero modificare questo codice in modo che i dati vengano inseriti in un altro thread e l'utente può ancora fare clic su un pulsante per visualizzare una sezione, senza importare i dati sospesi dall'interfaccia utente.

Qualsiasi aiuto sarebbe apprezzato Ho lavorato su questo problema per un paio di giorni e non sono stato in grado di risolverlo, anche con le altre domande simili qui. Se ci sono altre informazioni necessarie, fatemelo sapere!

Grazie

EDIT:

Finora questa domanda ha ricevuto uno verso il basso voto. Se c'è un modo per migliorare questa domanda, o qualcuno sa di una domanda che non sono stato in grado di trovare, potresti commentare come o dove? Se c'è un altro motivo per cui stai facendo downvoting, ti prego di farmelo sapere, poiché non sono in grado di capire la negatività e mi piacerebbe sapere come contribuire meglio.

risposta

11

Ci sono un paio di modi per questo.

Dal momento che si sta utilizzando UIManagedDocument si poteva usufruire di NSPrivateQueueConcurrencyType per inizializzare un nuovo NSManagedObjectContext e utilizzare performBlock per fare le tue cose. Ad esempio:

// 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]; 
}]; 

Quando si salva dal contesto, i dati del contesto privato vengono inseriti nel contesto corrente. Il salvataggio è visibile solo in memoria, quindi è necessario accedere al contesto principale (quello collegato allo UIDocument) e fare un salvataggio lì (dare un'occhiata a does-a-core-data-parent-managedobjectcontext-need-to-share-a-concurrency-type-wi).

L'altro modo (il mio preferito) è creare una sottoclasse NSOperation e fare cose lì. Ad esempio, dichiarare una sottoclasse NSOperation come la seguente:

//.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... 
} 

Ora nel thread principale si può prevedere che il funzionamento a una coda simile al seguente:

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

P.S. Non è possibile avviare un'operazione asincrona nel metodo main di NSOperation. Al termine di main, i delegati collegati a tali operazioni non verranno richiamati. Per dire la verità si può, ma questo comporta affrontare il ciclo di esecuzione o il comportamento concorrente.

Spero che questo aiuti.

+2

Sei una leggenda assoluta! Ho usato la tua prima soluzione, perché a me sembrava più semplice capire. Purtroppo non posso darti l'upvote che meriti per questo, visto che mi sono appena iscritto, ma quando posso lo farò. Guarderò NSOperations in un secondo momento, dato che mi ha un po 'confuso. Ancora una volta, molte grazie per il tuo aiuto! –

+0

Prego. Saluti. –

+2

Solo per il completamento: non dimenticare di dire al tuo UIManagedDocument che si trova in uno stato sporco dopo l'operazione (e ogni altra). Quindi forzare un salvataggio chiamando '[document updateChangeCount: UIDocumentChangeDone]'. Questo è ciò che manca nel corso Stanford CS193P. –

0

Inizialmente stavo per lasciare un commento, ma credo di non avere i privilegi per questo. Volevo solo far notare l'UIDocument, al di là del numero di cambiamento offre - (void)autosaveWithCompletionHandler:(void (^)(BOOL success))completionHandler

Il che non dovrebbe avere il ritardo che ho vissuto con l'aggiornamento del conteggio cambiamento come l'attesa di un "momento opportuno".

+0

questa non è una risposta – johannes

Problemi correlati