12 Stimmen

NSOperation blockiert UI-Malerei?

Ich suche Ratschläge für die Verwendung von NSOperation und Zeichnung:

Ich habe einen Hauptthread, der meine NSOperation Unterklasse, die sie dann in eine NSOperationQueue .

Meine NSOperation einige schwere Verarbeitung, ist es beabsichtigt, in seiner main()-Methode für mehrere Minuten Schleife, ständig Verarbeitung einige Arbeit, aber für jetzt habe ich nur eine while()-Schleife mit einem sleep(1) innerhalb, die eingestellt ist, um nur 5 Mal (für die Prüfung) gehen.

Das (ursprüngliche) Hauptthema, aus dem dies hervorgeht NSOperation ist für das Zeichnen zu einer Ansicht und die Aktualisierung der Benutzeroberfläche verantwortlich.

Ich wollte, dass der NSOperation-Thread eine Benachrichtigung verwendet, um dem Hauptthread mitzuteilen, dass er eine gewisse Menge an Verarbeitung durchgeführt hat. Im Moment wird diese Benachrichtigung einmal jedes Mal gesendet, wenn er seine while()-Schleife durchläuft (d.h. einmal pro Sekunde, weil er gerade sleep(1) macht). Der Hauptthread (die Ansicht) registriert sich, um diese Benachrichtigungen zu erhalten.

Die Benachrichtigungen werden sofort an den Hauptthread weitergeleitet und scheinen asynchron zu sein, was sehr gut aussieht. Es scheint, dass beide Threads wie erwartet ausgeführt werden... das heißt - gleichzeitig. (Ich verwende NSLog(), um grob zu überprüfen, wann jeder Thread die Benachrichtigung sendet und empfängt).

Wenn die Ansicht eine Benachrichtigung erhält und ihre Handler-Methode aufgerufen wird, erhöhe ich einfach eine Integer-Variable und versuche, diese in die Ansicht zu zeichnen (natürlich als String). Beim Testen zeichnet der Code in drawRect: diese Ganzzahl (als Zeichenkette) auf dem Bildschirm genau richtig.

Jedoch: hier ist mein Problem (sorry es ist eine kleine Weile gedauert, um hier zu erhalten): wenn der Haupt-Thread (Ansicht) eine Benachrichtigung von der NSOperation erhält, aktualisiert es diese Test-Integer und ruft [self setNeedsDisplay]. Die Ansicht wird jedoch nicht neu gezeichnet, bis die NSOperation beendet ist! Ich hatte erwartet, dass die NSOperation als separater Thread nicht in der Lage sein würde, die Ereignisschleife des Hauptthreads zu blockieren, aber es scheint, dass genau das passiert. Wenn die NSOperation beendet ist und ihre main() zurückkehrt, zeichnet sich die Ansicht schließlich sofort neu.

Vielleicht verwende ich nicht NSOperation richtig. Ich verwende es im "nicht-gleichzeitigen" Modus, aber trotz des Namens ist mein Verständnis, dass dies immer noch einen neuen Thread erzeugt und eine asynchrone Verarbeitung ermöglicht.

Ich bin für jede Hilfe und jeden Rat dankbar. Wenn Sie einen Code sehen möchten, lassen Sie es mich wissen.

10voto

gerry3 Punkte 21330

Die Methode im Beobachter, die als Reaktion auf Ihre Benachrichtigung ausgeführt wird, wird nicht auf dem Hauptthread ausgeführt.

In dieser Methode können Sie also eine andere Methode dazu zwingen, auf dem Hauptthread zu laufen, indem Sie performSelectorOnMainThread:withObject:waitUntilDone: .

Zum Beispiel:

MyOperation.m

- (void)main {
    for (int i = 1; i <= 5; i++) {
        sleep(1);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"GTCNotification" object:[NSNumber numberWithInteger:i]];
    }
}

MyViewController.m

- (void)setupOperation {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myNotificationResponse:) name:@"GTCNotification" object:nil];

    NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
    MyOperation *myOp = [[MyOperation alloc] init];

    [opQueue addOperation:myOp];

    [myOp release];
    [opQueue release];
}

- (void)myNotificationResponse:(NSNotification*)note {
    NSNumber *count = [note object];
    [self performSelectorOnMainThread:@selector(updateView:) withObject:count waitUntilDone:YES];
}

- (void)updateView:(NSNumber*)count {
    countLabel.text = count.stringValue;
}

0voto

Daniel Punkte 568

Die meisten Menschen wissen, dass alle Aufgaben, die mit der Anzeige zusammenhängen, auf dem Haupt-Thread ausgeführt werden müssen. Als direkte Folge davon muss jedoch jede Änderung einer Cocoa-Bindungseigenschaft, die sich auf das Zeichnen auswirken kann, auch auf dem Haupt-Thread geändert werden (da die KVO-Trigger auf dem Thread behandelt werden, von dem aus sie ausgelöst werden). Dies ist für die meisten Leute eine Überraschung: insbesondere ist es nicht sicher, [self setNeedsDisplay] von einem anderen Thread als dem Hauptthread aufzurufen.

Wie von Gerry erwähnt, wird Ihre NSNotification nicht im Hauptthread verarbeitet, sondern in dem Thread, von dem aus sie gesendet wird. Daher müssen Sie in Ihrem NSNotification-Handler Ihre Befehle an den Hauptthread zurücksenden, wenn sie sich auf die Anzeige beziehen oder wenn sie Cocoa-Bindungen betreffen. @performSelector funktioniert, aber mit Warteschlangen habe ich eine einfachere und besser lesbare Methode gefunden:

[ [ NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
    /* Your main-thread code here */ }];

Sie vermeidet die Definition einer sekundären Hilfsfunktion, deren einziger Zweck es ist, vom Hauptthread aufgerufen zu werden. mainQueue wurde erst in >10.6 definiert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X