Pregunta

Soy incapaz de encontrar una buena documentación sobre cómo subclase NSOperation a ser concurrentes y también para la cancelación de soporte. He leído la documentación de Apple, pero no puedo encontrar un ejemplo "oficial".

Aquí está mi código fuente:

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

En el ejemplo que he encontrado, no entiendo por qué performSelectorOnMainThread: se utiliza. Se evitaría mi operación se ejecute al mismo tiempo.

Además, cuando comento hacia fuera de esa línea, le ponga las operaciones se ejecutan simultáneamente. Sin embargo, la bandera isCancelled no se modifica, a pesar de que he llamado cancelAllOperations.

¿Fue útil?

Solución

Muy bien, así como yo lo entiendo, tiene dos preguntas:

  1. ¿Necesita el segmento performSelectorOnMainThread: que aparece en comentarios en el código? ¿Qué hace que el código?

  2. ¿Por qué la bandera _isCancelled no se modifica cuando se llama cancelAllOperations en el NSOperationQueue que contiene esta operación?

El acuerdo de Let con éstos a fin. Voy a asumir que su subclase de NSOperation se llama MyOperation, sólo para facilitar la explicación. Voy a explicar lo que estás mal entendido y luego darle un ejemplo corregido.

1. NSOperations se ejecutan simultáneamente

La mayoría de las veces, va a utilizar NSOperations con un NSOperationQueue, ya partir de su código, parece que eso es lo que está haciendo. En ese caso, su MyOperation siempre se ejecuta en un subproceso en segundo plano, independientemente de lo que el -(BOOL)isConcurrent devuelve el método, ya que NSOperationQueues están diseñados expresamente para las operaciones se ejecutan en segundo plano.

Como tal, por lo general, no es necesario reemplazar el método -[NSOperation start], ya que por defecto simplemente invoca el método -main. Ese es el método que debe ser de primer orden. El método por defecto -start ya maneja el establecimiento isExecuting y isFinished para usted en los momentos adecuados.

Así que si quieres un NSOperation a ejecutarse en segundo plano, basta con reemplazar el método -main y lo puso en un NSOperationQueue.

El performSelectorOnMainThread: en el código haría que todas las instancias de MyOperation que siempre haga su tarea sobre el hilo principal. Dado que sólo una pieza de código puede estar ejecutándose en un hilo a la vez, esto significa que no hay otros MyOperations podrían estar en ejecución. Todo el propósito de NSOperation y NSOperationQueue es hacer algo en el fondo.

La única vez que desee forzar las cosas en el hilo principal es cuando se va a actualizar la interfaz de usuario. Si es necesario actualizar la interfaz de usuario cuando sus acabados MyOperation, que es cuándo se debe utilizar performSelectorOnMainThread:. Voy a mostrar cómo hacer eso en mi ejemplo a continuación.

2. Cancelación de una NSOperation

-[NSOperationQueue cancelAllOperations] llama al método -[NSOperation cancel], lo que hace que las llamadas posteriores a -[NSOperation isCancelled] a YES retorno. No obstante , que se han hecho dos cosas para hacer de este ineficaz.

  1. Está utilizando @synthesize isCancelled reemplazar el método de -isCancelled NSOperation. No hay ninguna razón para hacer esto. NSOperation ya implementa -isCancelled de una manera perfectamente aceptable.

  2. Estás mirando su propia variable de instancia _isCancelled para determinar si la operación ha sido cancelada. garantías NSOperation que [self isCancelled] volverá YES si la operación ha sido cancelada. Lo hace no garantía de que su método de selección personalizado se llama, ni que su propia variable de instancia es hasta la fecha. Usted debe comprobar [self isCancelled]

Lo que debe hacer

La cabecera:

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

Y la aplicación:

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

Tenga en cuenta que no es necesario hacer nada con isExecuting, isCancelled o isFinished. Los que se manejan de forma automática. Simplemente reemplazar el método -main. Es así de fácil.

(Nota:. Técnicamente, esto no es un NSOperation "concurrente", en el sentido de que -[MyOperation isConcurrent] volvería NO tal como se aplica por encima Sin embargo, puede ejecutar en un subproceso de fondo El isConcurrent. método realmente debería llamarse -willCreateOwnThread, ya que es una descripción más precisa de la intención del método.)

Otros consejos

La excelente respuesta de @BJHomer merece una actualización.

operaciones concurrentes deben reemplazar el método start en lugar de main.

Como se indica en la de Apple Documentación :

Si va a crear una operación simultánea, se debe redefinir los siguientes métodos y propiedades como mínimo:

  • start
  • asynchronous
  • executing
  • finished

Una aplicación adecuada también requiere para anular cancel también. Hacer una subclase seguro para subprocesos y conseguir la semántica requeridos derecho también es bastante complicado.

Por lo tanto, he puesto una subclase completa y trabajando como un propuesta implementada en Swift de revisión de código. Comentarios y sugerencias son bienvenidos.

Esta clase se puede utilizar fácilmente como una clase base para la clase de operación personalizada.

Sé que esto es una vieja pregunta, pero he estado investigando esto últimamente y se encontró con los mismos ejemplos y tenía las mismas dudas.

Si todo su trabajo se puede ejecutar de forma sincronizada dentro del método principal, que no necesita una operación simultánea, ni anulando inicio, acaba de hacer su trabajo y el retorno de la principal cuando haya terminado.

Sin embargo, si la carga de trabajo es asíncrona por naturaleza - es decir la carga de una NSURLConnection, debe subclase de empezar. Cuando sus devuelve el método de arranque, la operación no ha terminado todavía. Sólo se considera terminado por el NSOperationQueue cuando envía notificaciones manualmente MVA a las banderas y isfinished isExecuting (por ejemplo, una vez que los acabados de carga URL asíncrono o si falla).

Por último, uno que desee empezar su expedición al hilo principal cuando la carga de trabajo asíncrono desea iniciar requiere una escucha carrera bucle en el hilo principal. A medida que el trabajo en sí es asíncrona, no limitará su concurrencia, pero a partir de la obra en un subproceso de trabajo podría no tener una adecuada runloop listo.

Tome un vistazo a ASIHTTPRequest . Es un HTTP contenedor de clase construido encima de NSOperation como una subclase y parece poner en práctica estos. Tenga en cuenta que a partir de mediados de 2011, el desarrollador no recomienda el uso de ASI para nuevos proyectos.

En cuanto a definir " cancelado " propiedad (o definir " _cancelled " Ivar) en el interior NSOperation subclase, normalmente esto no es necesario. Simplemente porque cuando el usuario activa la cancelación, código personalizado siempre debe notificar a los observadores MVA que su operación es ahora finalizados con su trabajo. En otras palabras, isCancelled => isfinished.

En particular, cuando el objeto NSOperation depende de la realización de otros objetos de operación, controla la ruta de la clave isfinished para esos objetos. El no poder generar una notificación final ( en caso de cancelación sucede ), por tanto, puede impedir la ejecución de otras operaciones en su aplicación.


Por cierto, la respuesta de @BJ Homero: "El método isConcurrent debería ser nombre -willCreateOwnThread" tiene mucho sentido !

Porque si no anulan la puesta en método, sólo tiene que llamar manualmente por defecto a la puesta en el método de NSOperation-objeto, llamando a enhebrar sí es, por defecto, sincrónica; así, NSOperation a objetos es solamente una operación no concurrente.

Sin embargo, si lo hace anulación de puesta en método, en el interior puesta método de aplicación, la costumbre de código debe generar un hilo separado ... etc, entonces se rompe con éxito la restricción de "ser default-subproceso de llamada sincrónica", por lo tanto haciendo NSOperation-objeto convertirse en un concurrente-operación, que puede funcionar de forma asíncrona después.

Esta entrada de blog:

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

explica por qué es posible que necesite:

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

en su método start.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top