Ich muss eine Reihe von Download- und Datenbankschreibvorgängen in meiner App ausführen. Ich benutze das NSOperation
und NSOperationQueue
für das gleiche.
Dies ist ein Anwendungsszenario:
- Holen Sie sich alle Postleitzahlen von einem Ort.
- Holen Sie für jede Postleitzahl alle Häuser.
- Holen Sie sich für jedes Haus die Details der Bewohner
Wie gesagt, ich habe NSOperation
für jede Aufgabe eine definiert . Im ersten Fall (Task1) sende ich eine Anfrage an den Server, um alle Postleitzahlen abzurufen. Der Delegierte innerhalb der NSOperation
wird die Daten erhalten. Diese Daten werden dann in die Datenbank geschrieben. Die Datenbankoperation wird in einer anderen Klasse definiert. Von der NSOperation
Klasse aus rufe ich die in der Datenbankklasse definierte Schreibfunktion auf.
Meine Frage ist, ob der Datenbankschreibvorgang im Hauptthread oder in einem Hintergrundthread erfolgt. Als ich es innerhalb von a NSOperation
aufrief, erwartete ich, dass es in einem anderen Thread (Not MainThread) als dem ausgeführt wird NSOperation
. Kann jemand bitte dieses Szenario im Umgang mit NSOperation
und erklären NSOperationQueue
.
NSOperation
nochNSOperationQueue
delegierte Eigenschaften enthalten.Antworten:
Wenn Sie eine
NSOperationQueue
von Grund auf neu erstellen wie in:NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
Es wird in einem Hintergrund-Thread sein:
Es sei denn, Sie verwenden
mainQueue
:NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
Sie können auch Code wie diesen sehen:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init]; [myQueue addOperationWithBlock:^{ // Background work [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Main thread work (UI usually) }]; }];
Und die GCD-Version:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { // Background work dispatch_async(dispatch_get_main_queue(), ^(void) { // Main thread work (UI usually) }); });
NSOperationQueue
gibt feinere Kontrolle mit dem, was Sie tun möchten. Sie können Abhängigkeiten zwischen den beiden Vorgängen erstellen (herunterladen und in der Datenbank speichern). Um die Daten zwischen einem Block und dem anderen zu übertragen, können Sie beispielsweise davon ausgehen, dass aNSData
vom Server kommt, also:__block NSData *dataFromServer = nil; NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init]; __weak NSBlockOperation *weakDownloadOperation = downloadOperation; [weakDownloadOperation addExecutionBlock:^{ // Download your stuff // Finally put it on the right place: dataFromServer = .... }]; NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init]; __weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation; [weakSaveToDataBaseOperation addExecutionBlock:^{ // Work with your NSData instance // Save your stuff }]; [saveToDataBaseOperation addDependency:downloadOperation]; [myQueue addOperation:saveToDataBaseOperation]; [myQueue addOperation:downloadOperation];
Bearbeiten: Warum ich eine
__weak
Referenz für die Operationen verwende, finden Sie hier . Kurz gesagt, es ist jedoch zu vermeiden, Haltezyklen beizubehalten.quelle
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
und dass die NSOperationQueue im Hauptthread nicht angehalten werden kann.Wenn Sie den Datenbankschreibvorgang im Hintergrundthread ausführen möchten, müssen Sie einen
NSManagedObjectContext
für diesen Thread erstellen .Sie können den Hintergrund
NSManagedObjectContext
in der Startmethode Ihrer jeweiligenNSOperation
Unterklasse erstellen .Überprüfen Sie die Apple-Dokumente auf Parallelität mit Kerndaten.
Sie können auch eine erstellen
NSManagedObjectContext
, die Anforderungen in einem eigenen Hintergrundthread ausführt, indem Sie sie mitNSPrivateQueueConcurrencyType
ihrerperformBlock:
Methode erstellen und die Anforderungen innerhalb ihrer Methode ausführen .quelle
Aus NSOperationQueue
Damit,
[NSOperationQueue mainQueue] // added operations execute on main thread [NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread
In Ihrem Fall möchten Sie möglicherweise Ihren eigenen "Datenbank-Thread" erstellen, indem Sie eine Unterklasse erstellen
NSThread
und Nachrichten an diesen sendenperformSelector:onThread:
.quelle
Der Ausführungsthread von NSOperation hängt davon ab,
NSOperationQueue
wo Sie die Operation hinzugefügt haben. Achten Sie auf diese Aussage in Ihrem Code -[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class
All dies setzt voraus, dass Sie kein weiteres Threading durchgeführt haben
main
, beiNSOperation
dem es sich um das eigentliche Monster handelt, in das die Arbeitsanweisungen geschrieben wurden (voraussichtlich).Bei gleichzeitigen Vorgängen ist das Szenario jedoch anders. Die Warteschlange kann für jede gleichzeitige Operation einen Thread erzeugen. Obwohl es nicht garantiert ist und von den Systemressourcen im Vergleich zu den Anforderungen an die Betriebsressourcen an diesem Punkt im System abhängt . Sie können die Parallelität der Operationswarteschlange anhand ihrer
maxConcurrentOperationCount
Eigenschaft steuern .BEARBEITEN -
Ich fand Ihre Frage interessant und habe selbst einige Analysen / Protokolle durchgeführt. Ich habe NSOperationQueue im Hauptthread wie folgt erstellt -
self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease]; NSLog(@"Operation queue creation. current thread = %@ \n main thread = %@", [NSThread currentThread], [NSThread mainThread]); self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency
Anschließend erstellte ich eine NSOperation und fügte sie mit addOperation hinzu. In der Hauptmethode dieser Operation, als ich nach dem aktuellen Thread gesucht habe,
NSLog(@"Operation obj = %@\n current thread = %@ \n main thread = %@", self, [NSThread currentThread], [NSThread mainThread]);
Es war nicht als Hauptthema. Und festgestellt, dass das aktuelle Thread-Objekt kein Haupt-Thread-Objekt ist.
Das benutzerdefinierte Erstellen einer Warteschlange im Hauptthread (ohne Parallelität zwischen den Vorgängen) bedeutet also nicht unbedingt, dass die Vorgänge seriell im Hauptthread selbst ausgeführt werden.
quelle
[[NSOperationQueue alloc] init]
sicher, dass die Warteschlange eine zugrunde liegende globale DispatchQueue mit Standardhintergrund verwendet. Daher wird jede benutzerdefinierte initialisierte OperationQueue ihre Aufgaben in DispatchQueues im Hintergrund einspeisen und Aufgaben daher in Threads einspeisen, die nicht der Hauptthread sind. Um Operationen in den Hauptthread einzuspeisen, müssten Sie die Hauptoperationswarteschlange mit so etwas wie erhalten[NSOperationQueue mainQueue]
. Dadurch wird die Hauptoperationswarteschlange abgerufen, die die Haupt-DispatchQueue intern verwendet, und daher schließlich Aufgaben an den Hauptthread weitergeleitet.Die Zusammenfassung aus den Dokumenten lautet
operations are always executed on a separate thread
(nach iOS 4 impliziert GCD zugrunde liegende Operationswarteschlangen).Es ist trivial zu überprüfen, ob es tatsächlich auf einem Nicht-Haupt-Thread ausgeführt wird:
NSLog(@"main thread? %@", [NSThread isMainThread] ? @"YES" : @"NO");
Wenn Sie in einem Thread ausgeführt werden, ist es trivial, GCD / libdispatch zu verwenden, um etwas auf dem Hauptthread auszuführen, unabhängig davon, ob Kerndaten, Benutzeroberfläche oder anderer Code für die Ausführung auf dem Hauptthread erforderlich sind:
dispatch_async(dispatch_get_main_queue(), ^{ // this is now running on the main thread });
quelle
Wenn Sie nicht triviales Threading durchführen, sollten Sie FMDatabaseQueue verwenden .
quelle