Subklassen NSOperation werden gleichzeitig und stornierbar
-
27-09-2019 - |
Frage
Ich bin nicht in der Lage eine gute Dokumentation darüber zu finden, wie NSOperation
Unterklasse gleichzeitig zu sein und auch Unterstützung Stornierung. Ich lese die Apple-docs, aber ich bin nicht in der Lage ein „offizielles“ Beispiel zu finden.
Hier ist mein Quellcode:
@synthesize isExecuting = _isExecuting;
@synthesize isFinished = _isFinished;
@synthesize isCancelled = _isCancelled;
- (BOOL)isConcurrent
{
return YES;
}
- (void)start
{
/* WHY SHOULD I PUT THIS ?
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
*/
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];
if (_isCancelled == YES)
{
NSLog(@"** OPERATION CANCELED **");
}
else
{
NSLog(@"Operation started.");
sleep(1);
[self finish];
}
}
- (void)finish
{
NSLog(@"operationfinished.");
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
if (_isCancelled == YES)
{
NSLog(@"** OPERATION CANCELED **");
}
}
Im Beispiel fand ich, ich verstehe nicht, warum performSelectorOnMainThread: verwendet wird. Es wäre mir eine Operation verhindern, gleichzeitig ausgeführt werden.
Auch wenn ich diese Zeile auf Kommentar, erhalte ich meine Operationen gleichzeitig ausgeführt werden. Allerdings ist die isCancelled
Flagge nicht geändert, obwohl ich cancelAllOperations
genannt haben.
Lösung
Okay, so wie ich es verstehe, haben Sie zwei Fragen:
-
Sie benötigen das
performSelectorOnMainThread:
Segment, das erscheint in den Kommentaren im Code? Was bedeutet, dass Code tun? -
Warum ist die
_isCancelled
Flag wird nicht geändert, wenn SiecancelAllOperations
auf derNSOperationQueue
aufrufen, die diese Operation enthält?
Lassen Sie uns beschäftigen sich mit diesen um. Ich gehe davon aus, dass Ihre Unterklasse von NSOperation
MyOperation
genannt wird, nur für eine einfache Erklärung. Ich werde erklären, was Sie Missverständnisse und dann ein korrigiertes Beispiel.
1. Lauf NSOperations gleichzeitig
Die meiste Zeit werden Sie NSOperation
s mit einem NSOperationQueue
zu verwenden, und aus dem Code, es klingt wie das ist, was du tust. In diesem Fall wird Ihr MyOperation
immer auf einem Hintergrund-Thread ausgeführt werden, unabhängig davon, was die -(BOOL)isConcurrent
Methode zurückgibt, wird seit NSOperationQueue
s explizit im Hintergrund laufen Operationen ausgelegt.
Als solches Sie im Allgemeinen nicht brauchen die -[NSOperation start]
Methode außer Kraft zu setzen, da standardmäßig einfach die -main
Methode aufruft. Das ist die Methode, die Sie überschreiben werden soll. Die Standard -start
Methode bereits Griffe isExecuting
und isFinished
für Sie zu den entsprechenden Zeiten einstellen.
Wenn Sie also ein NSOperation
wollen im Hintergrund laufen zu lassen, einfach die -main
Methode außer Kraft setzen und es auf einem NSOperationQueue
setzen.
Die performSelectorOnMainThread:
im Code würde jede Instanz MyOperation
verursachen immer seine Aufgabe auf dem Haupt-Thread ausführen. Da nur ein Stück Code kann zu einem Zeitpunkt auf einem Thread ausgeführt werden, bedeutet dies, dass keine andere MyOperation
s ausgeführt werden können. Der ganze Zweck der NSOperation
und NSOperationQueue
ist etwas im Hintergrund zu tun.
Das einzige Mal, wenn Sie die Dinge auf den Hauptthread zwingen wollen, ist, wenn Sie die Benutzeroberfläche sind zu aktualisieren. Wenn Sie die Benutzeroberfläche zu aktualisieren, wenn Ihre MyOperation
beendet, , die ist, wenn Sie performSelectorOnMainThread:
verwenden sollten. Ich werde zeigen, wie das unten in meinem Beispiel zu tun.
2. ein Abbrechen NSOperation
-[NSOperationQueue cancelAllOperations]
ruft die -[NSOperation cancel]
Methode, die nachfolgenden Aufrufe -[NSOperation isCancelled]
auf Rückkehr YES
verursacht. Doch , haben Sie zwei Dinge tun dies unwirksam zu machen.
-
Sie verwenden
@synthesize isCancelled
NSOperation der-isCancelled
Methode außer Kraft zu setzen. Es gibt keinen Grund, dies zu tun.NSOperation
bereits implementiert-isCancelled
in einer durchaus akzeptabel Weise. -
Sie Ihr eigenes
_isCancelled
Instanz-Variable überprüft, um festzustellen, ob der Vorgang abgebrochen wurde.NSOperation
garantiert, dass[self isCancelled]
YES
zurück, wenn der Vorgang abgebrochen wurde. Es tut nicht Garantie, dass Ihre individuelle Setter-Methode aufgerufen wird, noch, dass die eigene Instanz-Variable ist aktuell. Sie sollten[self isCancelled]
checken
Was Sie tun sollten
Der Header:
// MyOperation.h
@interface MyOperation : NSOperation {
}
@end
Und die Umsetzung:
// MyOperation.m
@implementation MyOperation
- (void)main {
if ([self isCancelled]) {
NSLog(@"** operation cancelled **");
}
// Do some work here
NSLog(@"Working... working....")
if ([self isCancelled]) {
NSLog(@"** operation cancelled **");
}
// Do any clean-up work here...
// If you need to update some UI when the operation is complete, do this:
[self performSelectorOnMainThread:@selector(updateButton) withObject:nil waitUntilDone:NO];
NSLog(@"Operation finished");
}
- (void)updateButton {
// Update the button here
}
@end
Beachten Sie, dass Sie nichts tun müssen, um mit isExecuting
, isCancelled
oder isFinished
. Das ist alles automatisch für Sie behandelt. außer Kraft setzen Sie einfach die -main
Methode. Es ist so einfach.
(Eine Anmerkung:. Technisch ist dies keine „concurrent“ NSOperation
, in dem Sinne, dass -[MyOperation isConcurrent]
NO
wie oben umgesetzt zurückkehren würde es aber wird auf einem Hintergrund-Thread ausgeführt werden, um die isConcurrent
. Methode wirklich -willCreateOwnThread
benannt werden soll, als dass eine genauere Beschreibung der Absicht des Verfahrens ist.)
Andere Tipps
Die ausgezeichnete Antwort von @BJHomer verdient ein Update.
Concurrent Operationen sollten die start
Methode statt main
außer Kraft setzen.
Wie bereits erwähnt in der Apple-Dokumentation :
Wenn Sie eine gleichzeitige Operation erstellen, müssen Sie die folgenden Methoden und Eigenschaften auf einem Minimum außer Kraft zu setzen:
-
start
-
asynchronous
-
executing
-
finished
Eine ordnungsgemäße Umsetzung auch erfordert cancel
auch außer Kraft zu setzen. Erstellen einer Unterklasse thread-safe und die erforderlichen Semantik richtig hinzubekommen ist auch ziemlich schwierig.
So habe ich lege eine vollständige und Arbeitsunterklasse als Vorschlag in Swift implementiert in Code Review. Kommentare und Anregungen sind willkommen.
Diese Klasse kann leicht als Basisklasse für benutzerdefinierte Operation Klasse verwendet werden.
Ich weiß, das ist eine alte Frage, aber ich habe diese untersucht, in letzter Zeit und die gleichen Beispiele und hatte die gleichen Bedenken begegnet.
Wenn Sie Ihre Arbeit kann innerhalb des Hauptverfahrens synchron ausgeführt werden, müssen Sie nicht einen gleichzeitigen Betrieb benötigen, weder ein Start überschreiben, tun nur Ihre Arbeit und Rückkehr aus dem Haupt wenn Sie fertig sind.
Allerdings, wenn Ihre Arbeitsbelastung von Natur aus asynchron ist - das heißt eine NSURLConnection geladen haben, müssen Sie Unterklasse starten. Wenn Ihre Start-Methode zurückgibt, wird der Vorgang noch nicht abgeschlossen. Es wird nur durch die NSOperationQueue abgeschlossen betrachtet werden, wenn Sie manuell KVO Benachrichtigungen an der isfinished und isExecuting Flags (zum Beispiel einmal die async URL Laden beendet oder nicht).
sendenSchließlich könnte man auf den Hauptthread zum Versand gestartet werden soll, wenn die Async Workload Sie einen Laufschleife hören auf dem Hauptthread erfordern beginnen soll. Da die Arbeit selbst asynchron ist, wird sie Ihre Gleichzeitigkeit nicht beschränken, sondern die Arbeit in einem Arbeitsthread beginnen könnte keine richtige Runloop bereit hat.
Hier finden Sie aktuelle ASIHTTPRequest . Es ist eine HTTP-Wrapper-Klasse auf den NSOperation
als Unterklasse gebaut und scheint diese zu implementieren. Beachten Sie, dass ab Mitte 2011 empfiehlt der Entwickler mit ASI nicht für neue Projekte.
In Bezug auf definieren " gelöscht " Eigenschaft (oder definieren " _cancelled " Ivar) innerhalb NSOperation Unterklasse, in der Regel, die nicht notwendig ist. Ganz einfach, weil, wenn USER die Aufhebung auslöst, sollte benutzerdefinierter Code immer benachrichtigt KVO Beobachter, dass Ihr Betrieb jetzt ist beendet mit seiner Arbeit. Mit anderen Worten: isCancelled => isfinished.
Insbesondere dann, wenn NSOperation Objekt auf dem Abschluss anderen Betrieb Objekte abhängig ist, überwacht er den isfinished Schlüsselpfad für diese Objekte. Abschluss und Meldung erzeugen Failing ( im Fall einer Stornierung geschieht ) kann daher die Ausführung anderer Operationen in der Anwendung verhindern.
BTW, @BJ Homers Antwort: "Die isConcurrent Methode sollte wirklich genannt -willCreateOwnThread sein" macht viel Sinn !
Weil, wenn Sie NICHT Start-Methode tun außer Kraft setzen, einfach manuell aufrufen NSOperation-Object-Standard-Start-Methode, Aufruf-Thread selbst ist, standardmäßig synchron; so, NSOperation-Objekt ist nur ein nicht-gleichzeitiger Betrieb.
Wenn Sie jedoch überschreiben Start-Methode DO, innerhalb Start-Methode Implementierung, benutzerdefinierten Code sollte einen separaten Thread laichen ... etc, dann brechen Sie erfolgreich die Einschränkung von „Aufruf-Thread-Standard Wesen synchron“, also ein gleichzeitiger-Betrieb immer, NSOperation-Objekt macht es asynchron danach ausgeführt werden kann.
Dieser Blog-Eintrag:
http://www.dribin.org/dave / Blog / Archiv / 2009/09/13 / snowy_concurrent_operations /
erklärt, warum Sie benötigen:
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
in Ihrer start
Methode.