2012-06-11 17 views
5

Das ist meine erste Frage zu Stack Overflow, also bitte entschuldigt mich, wenn ich irgendeine Etikette breche. Ich bin auch ziemlich neu in der Objective-C/App-Erstellung.UIManagedDocument fügt Objekte in den Hintergrund ein Thread

Ich habe den CS193P Stanford Kurs, insbesondere die CoreData Vorlesungen/Demos, verfolgt. In der Photomania-App von Paul Hegarty beginnt er mit einer Tabellenansicht und füllt die Daten im Hintergrund auf, ohne den UI-Fluss zu unterbrechen. Ich habe eine Anwendung erstellt, die Unternehmen im lokalen Bereich auflistet (aus einer API, die JSON-Daten zurückgibt).

Ich habe die Kategorien nach Pauls Foto-/Fotografenklassen erstellt. Die Erstellung der Klassen selbst ist kein Problem, dort werden sie erstellt.

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

Meine Anwendung beginnt mit einem UIViewController mit mehreren Tasten, von denen jede eine Tableview für den entsprechenden Abschnitt öffnet (diese alle gut funktionieren, ich versuche, genügend Informationen zur Verfügung zu stellen, so dass meine Frage Sinn macht). Ich rufe eine Hilfsmethode zum Erstellen/Öffnen der URL für das UIManagedDocument auf, das auf this question basiert. Dies wird aufgerufen, sobald die Anwendung ausgeführt wird, und es wird schnell geladen.

Ich habe eine Methode sehr ähnlich wie Paul fetchFlickrDataIntoDocument:

-(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] analysieren nur die JSON-Daten und gibt eine NSArray einzelne businessses enthält.

Ich habe den Code mit einem NSLog am Anfang und Ende des Business-Erstellung-Code ausgeführt. Jedem Unternehmen ist ein Abschnitt zugewiesen, für dessen Erstellung 0,006 Sekunden benötigt werden, und es gibt mehrere hundert davon. Der Einsatz endet etwa 2 Sekunden.

Die Helper-Methode ist hier:

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

Und in viewDidLoad genannt wird:

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

useDocument nur setzt self.document der bereitgestellten Dokument.

Ich möchte diesen Code ändern, so dass die Daten in einen anderen Thread eingefügt werden, und der Benutzer kann immer noch auf eine Schaltfläche klicken, um einen Abschnitt anzuzeigen, ohne den Datenimport hängen die Benutzeroberfläche.

Jede Hilfe wäre willkommen Ich habe an diesem Thema für ein paar Tage gearbeitet und konnte es nicht lösen, auch nicht mit den anderen ähnlichen Fragen hier. Wenn Sie weitere Informationen benötigen, lassen Sie es mich bitte wissen!

Danke

EDIT:

Bisher in dieser Frage eine nach unten Abstimmung erhalten hat. Wenn es einen Weg gibt, diese Frage zu verbessern, oder jemand eine Frage kennt, die ich nicht finden konnte, könntest du bitte kommentieren, wie oder wo? Wenn es einen anderen Grund für die Ablehnung gibt, lassen Sie es mich wissen, da ich die Negativität nicht verstehen kann und gerne lernen würde, besser beizutragen.

Antwort

11

Es gibt ein paar Möglichkeiten, dies zu tun.

Da Sie UIManagedDocument verwenden Sie die Vorteile von NSPrivateQueueConcurrencyType nehmen könnte initialisieren einen neuen NSManagedObjectContext und verwenden performBlock Ihre Sachen zu tun. Beispiel:

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

Beim Speichern aus dem Kontext werden Daten des privaten Kontexts in den aktuellen Kontext verschoben. Das Speichern ist nur im Speicher sichtbar. Sie müssen also auf den Hauptkontext (den mit UIDocument verknüpften) zugreifen und dort speichern (siehe does-a-core-data-parent-managedobjectcontext-need-to-share-a-concurrency-type-wi).

Der andere Weg (mein Favorit) ist es, eine NSOperation Unterklasse zu erstellen und dort Sachen zu tun. Zum Beispiel deklarieren eine NSOperation Unterklasse wie folgt aus:

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

Jetzt im Hauptthread Sie, dass der Betrieb in eine Warteschlange wie folgt zur Verfügung stellen kann:

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

P. S. Sie können eine asynchrone Operation in der main-Methode eines NSOperation nicht starten. Wenn die main beendet ist, werden Delegaten, die mit diesen Vorgängen verknüpft sind, nicht aufgerufen. Um die Wahrheit zu sagen, die Sie können, aber dies beinhaltet, Run-Loop oder gleichzeitiges Verhalten zu behandeln.

Hoffe, dass hilft.

+2

Sie sind eine absolute Legende! Ich habe deine erste Lösung benutzt, denn mir schien es einfacher zu verstehen. Leider kann ich dir die Aufwertung nicht geben, die du so verdient hast, da ich gerade erst beigetreten bin, aber wenn ich kann, werde ich es tun. Ich werde zu einem späteren Zeitpunkt in NSOperations schauen, da das mich ein wenig verwirrt. Nochmals vielen Dank für Ihre Hilfe! –

+0

Gern geschehen. Prost. –

+2

Nur für den Abschluss: Vergessen Sie nicht, Ihrem UIManagedDocument mitzuteilen, dass es sich nach Ihrer (und jeder anderen) Operation in einem schmutzigen Zustand befindet. Erzwinge also ein Speichern durch Aufruf von '[document updateChangeCount: UIDocumentChangeDone]'. Dies ist, was im CS193P Stanford Kurs fehlt. –

0

Am Anfang wollte ich nur einen Kommentar hinterlassen, aber ich denke, ich habe keine Privilegien dafür. Ich wollte nur die UIDocument darauf hin, jenseits der Zählerwechsel bietet - (void)autosaveWithCompletionHandler:(void (^)(BOOL success))completionHandler

, die nicht die Verzögerung sollte ich mit der Aktualisierung der Änderungszählwert erlebt habe, wie es für einen „geeigneten Moment“ wartet.

+0

Dies ist keine Antwort – johannes