Pergunta

Não consigo encontrar uma boa documentação sobre como subclasse NSOperation ser simultâneo e também apoiar o cancelamento. Eu li o Apple Docs, mas não consigo encontrar um exemplo "oficial".

Aqui está o meu código -fonte:

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

No exemplo que encontrei, não entendo por que o performeSelectoronMainthread: é usado. Isso impediria que minha operação fosse executada simultaneamente.

Além disso, quando comento essa linha, recebo minhas operações em execução simultaneamente. No entanto, o isCancelled A bandeira não é modificada, apesar de ter ligado cancelAllOperations.

Foi útil?

Solução

Ok, então, pelo que entendi, você tem duas perguntas:

  1. Você precisa do performSelectorOnMainThread: segmento que aparece nos comentários em seu código? O que esse código faz?

  2. Porque é o _isCancelled A bandeira não é modificada quando você liga cancelAllOperations no NSOperationQueue que contém esta operação?

Vamos lidar com isso em ordem. Vou assumir que sua subclasse de NSOperation é chamado MyOperation, apenas para facilitar a explicação. Vou explicar o que você está entendendo mal e depois darei um exemplo corrigido.

1. Executando as operações simultaneamente

Na maioria das vezes, você usará NSOperations com um NSOperationQueue, e pelo seu código, parece que é isso que você está fazendo. Nesse caso, seu MyOperation sempre será executado em um tópico de segundo plano, independentemente do que o -(BOOL)isConcurrent o método retorna, já NSOperationQueueS são projetados explicitamente para executar operações em segundo plano.

Como tal, você geralmente não precisa substituir o -[NSOperation start] método, pois por padrão, simplesmente invoca o -main método. Esse é o método que você deve substituir. O padrão -start O método já lida com a configuração isExecuting e isFinished para você nos momentos apropriados.

Então, se você quiser um NSOperation Para correr em segundo plano, basta substituir o -main método e coloque -o em um NSOperationQueue.

o performSelectorOnMainThread: em seu código causaria todos os casos de MyOperation para sempre executar sua tarefa no tópico principal. Como apenas uma parte do código pode estar em execução em um tópico de cada vez, isso significa que nenhum outro MyOperations poderia estar funcionando. Todo o propósito de NSOperation e NSOperationQueue é fazer algo em segundo plano.

A única vez que você deseja forçar as coisas para o tópico principal é quando você está atualizando a interface do usuário. Se você precisar atualizar a interface do usuário quando o seu MyOperation acabamentos, este é quando você deve usar performSelectorOnMainThread:. Vou mostrar como fazer isso no meu exemplo abaixo.

2. Cancelando uma NSOPERAÇÃO

-[NSOperationQueue cancelAllOperations] chama o -[NSOperation cancel] método, que causa chamadas subsequentes para -[NSOperation isCancelled] para retornar YES. No entanto, você fez duas coisas para tornar isso ineficaz.

  1. Você está usando @synthesize isCancelled para substituir a NSoperation -isCancelled método. Não há razão para fazer isso. NSOperation já implementa -isCancelled de uma maneira perfeitamente aceitável.

  2. Você está verificando o seu próprio _isCancelled variável de instância para determinar se a operação foi cancelada. NSOperation garante isso [self isCancelled] retornará YES Se a operação foi cancelada. Isso faz não Garanta que seu método de setter personalizado seja chamado, nem que sua própria variável de instância esteja atualizada. Você deveria estar verificando [self isCancelled]

O que você deveria estar fazendo

O cabeçalho:

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

E a implementação:

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

Observe que você não precisa fazer nada com isExecuting, isCancelled, ou isFinished. Todos esses são tratados automaticamente para você. Simplesmente substituir o -main método. É tão fácil.

(Uma nota: tecnicamente, isso não é um "simultâneo" NSOperation, no sentido que -[MyOperation isConcurrent] retornaria NO como implementado acima. no entanto vai ser executado em um encadeamento de segundo plano. o isConcurrent Método realmente deve ser nomeado -willCreateOwnThread, pois essa é uma descrição mais precisa da intenção do método.)

Outras dicas

A excelente resposta do @bjhomer merece uma atualização.

Operações simultâneas devem substituir o start método em vez de main.

Conforme declarado no Documentação da Apple:

Se você estiver criando uma operação simultânea, precisará substituir os seguintes métodos e propriedades no mínimo:

  • start
  • asynchronous
  • executing
  • finished

Uma implementação adequada também requer para substituir cancel também. Fazendo uma subclasse discussão segura E acertar a semântica necessária também é bastante complicada.

Assim, eu coloquei uma subclasse completa e funcional como uma Proposta implementada em Swift na revisão do código. Comentários e sugestões são bem -vindos.

Esta classe pode ser facilmente usada como uma classe base para sua classe de operação personalizada.

Sei que essa é uma pergunta antiga, mas tenho investigado isso ultimamente e encontrei os mesmos exemplos e tive as mesmas dúvidas.

Se todo o seu trabalho puder ser executado de maneira síncrona dentro do método principal, você não precisará de uma operação simultânea, nem o Starting Start, basta fazer seu trabalho e retornar do Main quando terminar.

No entanto, se sua carga de trabalho for assíncrona por natureza - ou seja, carregando uma NSurlConnection, você deve subclasse começar. Quando o seu método de início retorna, a operação ainda não terminou. Ele será considerado finalizado apenas pelo NSoperationQueue quando você enviar manualmente as notificações KVO para os sinalizadores Isfinished e ISexecuting (por exemplo, uma vez que o URL assíncrono termina ou falhar).

Por fim, pode-se despachar iniciar o thread principal quando a carga de trabalho assíncrona que você deseja iniciar exige uma audição no Thread Principal. Como o trabalho em si é assíncrono, ele não limitará sua simultaneidade, mas iniciar o trabalho em um tópico de trabalhador pode não ter um Runloop adequado pronto.

Dar uma olhada em Asihttprequest. É uma classe de wrapper http construída sobre NSOperation como uma subclasse e parece implementá -los. Observe que, em meados de 2011, o desenvolvedor recomenda não usar o ASI para novos projetos.

Em relação a definir "cancelado"Propriedade (ou definir"_cancelado"Ivar) dentro da subclasse da NSoperation, normalmente isso não é necessário. Simplesmente porque, quando o usuário aciona o cancelamento, o código personalizado deve sempre notificar os observadores do KVO de que sua operação é agora finalizado com seu trabalho. Em outras palavras, iscanceled => isfinished.

Particularmente, quando o objeto NSoperation depende da conclusão de outros objetos de operação, ele monitora o caminho -chave ISfinished para esses objetos. Não gerar uma notificação de acabamento (Em caso de cancelamento acontece) pode, portanto, impedir a execução de outras operações em seu aplicativo.


BTW, a resposta de @BJ Homer: "O método IsConcurrent realmente deve ser Nomeado -willcreatewnhread "faz muito sentido!

Porque se você não substituir o método de início, basta ligar manualmente manualmente no padrão de Nsoperation-Object-start-method, o próprio thread de chamadas é, por padrão, síncrono; Portanto, o NSOPERATR-Object é apenas uma operação não concorrente.

No entanto, se você substituir o método inicial, na implementação do método de início, o código personalizado deve gerar um tópico separado… Etc, então você quebra com sucesso a restrição de "o padrão de thread de chamadas sendo síncrono", tornando-se, portanto, a NSoperation-Object se tornando uma operação simultânea, ele pode funcionar de forma assíncrona depois.

Este post do blog:

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

explica por que você pode precisar:

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

na tua start método.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top