Frage

Ich habe ein NSOperationQueue der 2 NSOperations enthält und eingestellt ist, sie eine nach der anderen durchzuführen, indem setMaxConcurrentOperationCount auf 1 setzen.

Eine der Operationen ist ein Standard nicht-gleichzeitiger Betrieb (nur eine main-Methode), die synchron einige Daten aus dem Web abgerufen (auf dem separaten Vorgang Faden natürlich). Die andere Operation ist eine gleichzeitige Operation, wie ich einige Code verwenden müssen, die asynchron ausgeführt hat.

Das Problem ist, dass ich entdeckt habe, dass der gleichzeitige Betrieb funktioniert nur, wenn es zuerst in die Warteschlange hinzugefügt wird. Wenn es nach irgendwelchen nicht-gleichzeitigen Operationen kommt, dann wird seltsam die start Methode fein genannt, aber nach, dass Verfahren endet, und ich habe meine Setup-Verbindung, ein Verfahren zum Rückruf, tut es nie. Keine weiteren Operationen in der Warteschlange nach erhalten ausgeführt. Es ist, als ob es nach dem Start-Methode zurückgibt hängt, und es werden keine Rückrufe von beliebigen URL-Verbindungen erhalten genannt!

Wenn mein gleichzeitiger Betrieb zunächst in der Warteschlange gestellt wird es dann alles funktioniert gut, die Asynchron-Rückrufe arbeiten und die nachfolgende Operation ausführt, nachdem es abgeschlossen ist. Ich verstehe gar nicht!

Sie können den Test-Code für meine gleichzeitige NSOperation siehe unten, und ich bin mir ziemlich sicher, dass es solide.

Jede Hilfe wäre sehr dankbar!

Hauptthread Beobachtung:

Ich habe gerade herausgefunden, dass, wenn der gleichzeitige Betrieb zunächst in der Warteschlange ist dann die [start] Methode auf dem Haupt-Thread aufgerufen wird. Wenn es jedoch nicht auf der ersten Warteschlange (wenn es nach entweder eine gleichzeitige oder nicht gleichzeitige ist), dann die [start] Verfahren nicht auf dem Haupt-Thread aufgerufen wird. Dies scheint wichtig, da sie das Muster meines Problems passen. Was ist der Grund dafür sein könnte?

Concurrent NSOperation Code:

@interface ConcurrentOperation : NSOperation {
    BOOL executing;
    BOOL finished;
}
- (void)beginOperation;
- (void)completeOperation;
@end

@implementation ConcurrentOperation
- (void)beginOperation {
    @try {

        // Test async request
        NSURLRequest *r = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]];
        NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:r delegate:self];
        [r release];

    } @catch(NSException * e) {
        // Do not rethrow exceptions.
    }
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSLog(@"Finished loading... %@", connection);
    [self completeOperation];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Finished with error... %@", error);
    [self completeOperation]; 
}
- (void)dealloc {
    [super dealloc];
}
- (id)init {
    if (self = [super init]) {

        // Set Flags
        executing = NO;
        finished = NO;

    }
    return self;
}
- (void)start {

    // Main thread? This seems to be an important point
    NSLog(@"%@ on main thread", ([NSThread isMainThread] ? @"Is" : @"Not"));

    // Check for cancellation
    if ([self isCancelled]) {
        [self completeOperation];
        return;
    }

    // Executing
    [self willChangeValueForKey:@"isExecuting"];
    executing = YES;
    [self didChangeValueForKey:@"isExecuting"];

    // Begin
    [self beginOperation];

}

// Complete Operation and Mark as Finished
- (void)completeOperation {
    BOOL oldExecuting = executing;
    BOOL oldFinished = finished;
    if (oldExecuting) [self willChangeValueForKey:@"isExecuting"];
    if (!oldFinished) [self willChangeValueForKey:@"isFinished"];
    executing = NO;
    finished = YES;
    if (oldExecuting) [self didChangeValueForKey:@"isExecuting"];
    if (!oldFinished) [self didChangeValueForKey:@"isFinished"];
}

// Operation State
- (BOOL)isConcurrent { return YES; }
- (BOOL)isExecuting { return executing; }
- (BOOL)isFinished { return finished; }

@end

Queuing-Code

// Setup Queue
myQueue = [[NSOperationQueue alloc] init];
[myQueue setMaxConcurrentOperationCount:1];

// Non Concurrent Op
NonConcurrentOperation *op1 = [[NonConcurrentOperation alloc] init];
[myQueue addOperation:op1];
[op1 release];

// Concurrent Op
ConcurrentOperation *op2 = [[ConcurrentOperation alloc] init];
[myQueue addOperation:op2];
[op2 release];
War es hilfreich?

Lösung

Ich habe entdeckt, was das Problem war!

Diese beiden von unschätzbarem Wert Artikel von Dave Dribin beschreiben gleichzeitige Vorgänge im Detail, sowie die Probleme, die Snow Leopard & das iPhone SDK einführen, wenn die Dinge asynchron aufrufen, die eine Laufschleife erfordern.

http://www.dribin.org/dave / Blog / Archiv / 2009/05/05 / concurrent_operations / http://www.dribin.org/dave/blog/ Archiv / 2009/09/13 / snowy_concurrent_operations /

Dank Chris Suter auch für mich in der richtigen Richtung!

Die Crux ist, um sicherzustellen, dass die start Methode uns auf dem Haupt-Thread genannt:

- (void)start {

    if (![NSThread isMainThread]) { // Dave Dribin is a legend!
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }

    [self willChangeValueForKey:@"isExecuting"];
    _isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];

    // Start asynchronous API

}

Andere Tipps

Ihr Problem ist höchstwahrscheinlich mit NSURLConnection. NSURLConnection hängt von einer Laufschleife, die einen bestimmten Modus ausgeführt wird (in der Regel nur die Standardeinstellung).

Es gibt eine Reihe von Lösungen für Ihr Problem:

  1. Stellen Sie sicher, dass dieser Vorgang nur auf dem Haupt-Thread ausgeführt wird. Wenn Sie unter OS X dies zu tun wurden, würden Sie überprüfen wollen, dass es das tut, was Sie in allen Lauf Loop-Modi wollen (zB modale und Event-Tracking-Modi), aber ich weiß nicht, was das Geschäft auf dem iPhone ist.

  2. Erstellen und Ihren eigenen Thread verwalten. Keine schöne Lösung.

  3. Anruf - [NSURLConnection scheduleInRunLoop: forMode:] und im Hauptthread übergeben oder einem anderen Thread, die Sie kennen. Wenn Sie dies tun, möchten Sie wahrscheinlich nennen - [NSURLConnection unscheduleInRunLoop: forMode:] zuerst sonst könnte man die Daten in mehreren Threads werden empfangen (oder zumindest das ist, was die Dokumentation scheint darauf hinzudeuten)

  4. .
  5. Verwenden Sie so etwas wie + [NSData dataWithContentsOfURL: Optionen: Fehler:]. Und das wird auch Ihr Betrieb vereinfachen, da Sie es sich um eine nicht-gleichzeitigen Betrieb stattdessen machen

  6. Variant auf # 4: Verwendung + [NSURLConnection sendSynchronousRequest: returningResponse: Fehler:].

Wenn Sie damit durchkommen können, tun # 4 oder # 5.

Ich habe nicht bemerkt, noch sehe ich jede Erwähnung von addDependency:, die wie eine Voraussetzung, um immer Operationen in der richtigen Reihenfolge auszuführen scheinen würde.

Kurz gesagt, die zweite Operation ist abhängig von dem ersten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top