Domanda

Non sono in grado di trovare una buona documentazione su come sottoclasse NSOperation di essere concorrente e anche per la cancellazione supporto. Ho letto la documentazione di Apple, ma non riesco a trovare un esempio "ufficiale".

Ecco il mio codice sorgente:

@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 **");
    }
}

Nell'esempio ho trovato, non capisco il motivo per cui performSelectorOnMainThread: viene utilizzato. Si impedirebbe la mia operazione di esecuzione contemporaneamente.

Inoltre, quando io commento che la linea, ho le mie operazioni in esecuzione contemporaneamente. Tuttavia, la bandiera isCancelled non viene modificato, anche se ho chiamato cancelAllOperations.

È stato utile?

Soluzione

Ok, se ho capito bene, si hanno due domande:

  1. Avete bisogno il segmento performSelectorOnMainThread: che appare nei commenti nel codice? Che cosa significa che il codice fare?

  2. Perché la bandiera _isCancelled non viene modificata quando si chiama cancelAllOperations sul NSOperationQueue che contiene questa operazione?

L'accordo di Let con questi in ordine. Ho intenzione di assumere che la vostra sottoclasse di NSOperation si chiama MyOperation, solo per facilità di spiegazione. Mi spiego quello che stai incomprensione e poi dare un esempio corretto.

1. NSOperations in esecuzione contemporaneamente

La maggior parte del tempo, che verrà utilizzato NSOperations con NSOperationQueue, e dal codice, suona come questo è quello che stai facendo. In tal caso, il MyOperation sarà sempre eseguito su un thread in background, indipendentemente da ciò il metodo restituisce -(BOOL)isConcurrent, dal momento che NSOperationQueues sono esplicitamente progettati per le operazioni eseguite in background.

In quanto tale, in genere non è necessario eseguire l'override del metodo -[NSOperation start], dal momento che per impostazione predefinita invoca semplicemente il metodo -main. Questo è il metodo che dovrebbe essere prevalente. Il metodo predefinito -start gestisce già l'impostazione isExecuting e isFinished per voi al momento opportuno.

Quindi, se si desidera un NSOperation per l'esecuzione in background, è sufficiente eseguire l'override del metodo -main e metterlo su un NSOperationQueue.

Il performSelectorOnMainThread: nel codice causerebbe ogni istanza di MyOperation di svolgere sempre il suo compito sul thread principale. Poiché solo un pezzo di codice può essere in esecuzione su un thread alla volta, questo significa che nessun altro MyOperations potrebbero essere in esecuzione. Lo scopo di NSOperation e NSOperationQueue è quello di fare qualcosa in background.

L'unica volta che si vuole forzare le cose sul thread principale è quando si sta aggiornando l'interfaccia utente. Se è necessario aggiornare l'interfaccia utente quando i vostri finiture MyOperation, che è quando si dovrebbe usare performSelectorOnMainThread:. Vi mostrerò come fare nel mio esempio qui sotto.

2. Annullamento di un NSOperation

-[NSOperationQueue cancelAllOperations] chiama il metodo -[NSOperation cancel], che provoca chiamate successive -[NSOperation isCancelled] a YES ritorno. Tuttavia , avete fatto due cose per rendere questo inefficace.

  1. Si utilizza @synthesize isCancelled di sovrascrivere il metodo -isCancelled di NSOperation. Non c'è alcuna ragione per farlo. NSOperation già implementa -isCancelled in modo perfettamente accettabile.

  2. Si sta verificando la propria variabile di istanza _isCancelled per determinare se l'operazione è stata annullata. garanzie NSOperation che [self isCancelled] restituirà YES se l'operazione è stata annullata. Lo fa non la garanzia che il vostro metodo setter personalizzato sarà chiamato, né che la variabile propria istanza sia aggiornato. Si dovrebbe essere il controllo [self isCancelled]

Cosa si dovrebbe fare

L'intestazione:

// MyOperation.h
@interface MyOperation : NSOperation {
}
@end

E l'attuazione:

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

Si noti che non è necessario fare nulla con isExecuting, isCancelled o isFinished. Quelle sono tutte gestite automaticamente per voi. È sufficiente eseguire l'override del metodo -main. E 'così facile.

(Nota:. Tecnicamente, questa non è una NSOperation "concorrente", nel senso che -[MyOperation isConcurrent] NO ritornerebbe come attuata in precedenza Tuttavia, verrà essere eseguito su un thread sfondo La isConcurrent. metodo in realtà dovrebbe essere chiamato -willCreateOwnThread, come quello è una descrizione più accurata della volontà del metodo.)

Altri suggerimenti

La risposta eccellente di @BJHomer merita un aggiornamento.

operazioni simultanee devono eseguire l'override del metodo start invece di main.

Come indicato nel di Apple Documentazione :

Se si sta creando un funzionamento in parallelo, è necessario sostituire i seguenti metodi e proprietà a un minimo:

  • start
  • asynchronous
  • executing
  • finished

Una corretta attuazione anche richiede per ignorare cancel pure. Fare una sottoclasse thread-safe e ottenere la semantica richiesti destra è anche abbastanza difficile.

Così, ho messo una sottoclasse completo e lavorare come proposta implementato in Swift in Code Review. Commenti e suggerimenti sono i benvenuti.

Questa classe può essere facilmente utilizzato come classe base per la classe un'operazione personalizzata.

So che questa è una vecchia questione, ma ho indagato questo ultimamente e ha incontrato gli stessi esempi e ha avuto gli stessi dubbi.

Se tutto il vostro lavoro può essere eseguito in modo sincrono all'interno del metodo principale, non è necessario un funzionamento in parallelo, né ignorando avvio, basta fare il vostro lavoro e di ritorno dalla principale quando fatto.

Tuttavia, se il carico di lavoro è asincrona per natura - cioè il caricamento di un NSURLConnection, è necessario creare una sottoclasse di iniziare. Quando il vostro metodo di avvio ritorna, l'operazione non è ancora finito. Sarà considerato terminato solo dal NSOperationQueue quando si invia manualmente KVO notifiche alle bandiere isfinished e isExecuting (per esempio, una volta che le finiture di caricamento asincrono URL o fallisce).

Infine, si potrebbe desiderare di iniziare la spedizione al thread principale quando il carico di lavoro asincrona si desidera avviare richiedono un ascolto run-ciclo sul thread principale. Come l'opera stessa è asincrona, non limitare la concorrenza, ma di iniziare il lavoro in un thread di lavoro potrebbe non avere un adeguato runloop pronto.

Date un'occhiata a ASIHTTPRequest . Si tratta di un HTTP wrapper di classe costruito in cima ad NSOperation come una sottoclasse e sembra implementare questi. Nota che a partire da metà del 2011, lo sviluppatore non raccomanda di utilizzare ASI per nuovi progetti.

Per quanto riguarda definire " annullato " proprietà (oppure definire " _cancelled " Ivar) all'interno NSOperation sottoclasse, di solito che non è necessario. Semplicemente perché quando utente attiva la cancellazione, codice personalizzato deve sempre notificare gli osservatori KVO che l'operazione è ora terminati con il suo lavoro. In altre parole, isCancelled => isfinished.

In particolare, quando l'oggetto NSOperation dipende dal completamento di altri oggetti di funzionamento, esso controlla il percorso della chiave isfinished per quegli oggetti. Non riuscendo a generare una notifica finitura ( in caso di cancellazione avviene ) può quindi impedire l'esecuzione di altre operazioni nell'applicazione.


A proposito, la risposta di @BJ Homer: "Il metodo isConcurrent in realtà dovrebbe essere nome -willCreateOwnThread" fa un sacco senso !

Perché se non prevalgono start-metodo, è sufficiente chiamare manualmente default-start-il metodo di NSOperation-Object, chiamando-filo è di per sé, per impostazione predefinita, sincrono; così, NSOperation-Object è solo un'operazione non simultanea.

Tuttavia, se si fa start-metodo di sostituzione, all'interno start-metodo di attuazione, su misura codice dovrebbe generare un thread separato ... ecc, allora si rompe con successo la restrizione di "essere predefinita chiamando-thread sincrona", rendendo quindi NSOperation-Object diventando un concorrente-operazione, può funzionare in modo asincrono in seguito.

Questo post del blog:

http://www.dribin.org/dave / blog / archives / 2009/09/13 / snowy_concurrent_operations /

spiega il motivo per cui potrebbe essere necessario:

if (![NSThread isMainThread])
{
    [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
    return;
}

nel metodo start.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top