Defino propriedades como nulo em desalocação ao usar o ARC?

125

Estou tentando aprender a contagem automática de referências no iOS 5. Agora, a primeira parte desta pergunta deve ser fácil:

  1. É correto que eu NÃO precise escrever instruções explícitas de propriedade de liberação em meu acordo quando usar o ARC? Em outras palavras, é verdade que o seguinte NÃO precisa de um desdobramento explícito?

    @interface MyClass : NSObject
    @property (strong, nonatomic) NSObject* myProperty;
    @end
    
    @implementation MyClass
    @synthesize myProperty;
    @end
    
  2. Minha próxima e mais importante pergunta vem de uma linha no documento Transitions to ARC Release Notes :

    Você não precisa (na verdade não pode) liberar variáveis ​​de instância, mas pode precisar chamar [self setDelegate: nil] nas classes do sistema e outro código que não é compilado usando o ARC.

    Isso levanta a questão: como sei quais classes de sistema não são compiladas com o ARC? Quando devo criar meu próprio desalocação e definir explicitamente propriedades de retenção forte para zero? Devo assumir que todas as classes de estrutura NS e UI usadas nas propriedades requerem acordos desastrosos explícitos?

Há uma abundância de informações sobre SO e em outras partes sobre as práticas de liberação de ivar de apoio de uma propriedade ao usar o rastreamento de referência manual, mas relativamente pouco sobre isso ao usar o ARC.

arrogante
fonte

Respostas:

197

Resposta curta : não, você não precisa anular propriedades no deallocARC.

Resposta longa : você nunca deve anular propriedades dealloc, mesmo no gerenciamento manual de memória.

No MRR, você deve liberar seus ivars . Recolher propriedades significa chamar os setters, que podem chamar o código que não deve ser tocado dealloc(por exemplo, se sua classe ou uma subclasse substitui o setter). Da mesma forma, pode disparar notificações do KVO. A liberação do ivar evita esses comportamentos indesejados.

No ARC, o sistema libera automaticamente qualquer ivars para você, portanto, se é isso que você está fazendo, nem precisa implementar dealloc. No entanto, se você tiver algum ivars que não seja objeto que precise de tratamento especial (por exemplo, buffers alocados que você precisa free()), ainda precisará lidar com aqueles dealloc.

Além disso, se você se definir como delegado de qualquer objeto, deverá desabilitar esse relacionamento dealloc(esse é o pouco sobre chamar [obj setDelegate:nil]). A observação de fazer isso em classes que não são compiladas com o ARC é um aceno para as propriedades fracas. Se a classe marcar explicitamente sua delegatepropriedade como weakentão você não precisará fazer isso, porque a natureza das propriedades fracas significa que elas serão detalhadas. No entanto, se a propriedade estiver marcada assign, você deve anulá-la na sua dealloc, caso contrário, a classe será deixada com um ponteiro pendente e provavelmente falhará se tentar enviar uma mensagem ao seu representante. Observe que isso se aplica apenas a relacionamentos não retidos, como delegados.

Lily Ballard
fonte
2
Isso faz sentido! Deixe-me perguntar-lhe o seguinte: um cenário comum que tenho é que tenho uma MyController : UIViewControllerclasse que cria e possui um UIView e também define o delegado da exibição para si mesmo. É o único proprietário retentor dessa opinião. Quando o controlador é desalocado, a visualização também deve ser desalocada. Importa então se o ponteiro do delegado está pendurado?
emfurry
4
@ pressa: Provavelmente não, porque quando o seu controlador de exibição morre, a visualização em si não deve estar na hierarquia da visualização e não deve estar fazendo nada, mas é melhor não fazer suposições. E se a exibição programada de forma assíncrona for executada posteriormente, e a exibição em si acabar perdendo seu controlador de exibição por um curto período de tempo (por exemplo, devido ao trabalho assíncrono manter a exibição temporariamente)? É melhor deixar o delegado sem segurança. De fato, se a exibição em questão for a UIWebView, os documentos declaram explicitamente que você precisa anular o delegado.
Lily Ballard
3
@zeiteisen: No. unsafe_unretainedé exatamente equivalente a uma assignpropriedade e é o comportamento normal dos relacionamentos de delegados no MRR, e esses precisam ser detalhados.
Lily Ballard
4
Não concordo com a afirmação de não usar setters em acordo com o MRC. A Apple não o recomenda, mas o faz em seu código também. Na verdade, você pode criar novos problemas não usando o setter. Existem várias grandes discussões sobre isso. O importante é escrever o levantador corretamente (ele deve se comportar corretamente se você passar um valor nulo) e, às vezes, observar a ordem da desalocação.
precisa
7
@Sulthan: Usar ou não setters em dealloc é uma enorme lata de worms, mas minha posição se resume basicamente a: você deseja chamar o mínimo de código possível em dealloc. Os levantadores tendem a incluir efeitos colaterais, substituindo nas subclasses ou via KVO, ou outros mecanismos. Os efeitos colaterais no acordo devem ser especialmente evitados como a praga. Se você puder remover uma chamada de método do dealloc, faça-o. Isso é simplificado para: não chame setters em desalocação.
Lily Ballard
2

Só para dar a resposta oposta ...

Resposta curta : não, você não precisa anular as propriedades auto-sintetizadas no deallocARC. E você não precisa usar o setter para aqueles que estão dentro init.

Resposta longa : você deve fornecer propriedades sintetizadas personalizadas dealloc, mesmo no ARC. E você deve usar o setter para aqueles que estão dentro init.

O ponto é que suas propriedades sintetizadas personalizadas devem ser seguras e simétricas em relação à anulação.

Um possível setter para um temporizador:

-(void)setTimer:(NSTimer *)timer
{
    if (timer == _timer)
        return;

    [timer retain];
    [_timer invalidate];
    [_timer release];
    _timer = timer;
    [_timer fire];
}

Um possível setter para uma exibição de rolagem, tableview, webview, campo de texto, ...:

-(void)setScrollView:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView)
        return;

    [scrollView retain];
    [_scrollView setDelegate:nil];
    [_scrollView release];
    _scrollView = scrollView;
    [_scrollView setDelegate:self];
}

Um possível setter para uma propriedade KVO:

-(void)setButton:(UIButton *)button
{
    if (button == _button)
        return;

    [button retain];
    [_button removeObserver:self forKeyPath:@"tintColor"];
    [_button release];
    _button = button;
    [_button addObserver:self forKeyPath:@"tintColor" options:(NSKeyValueObservingOptions)0 context:NULL];
}

Então você não tem que duplicar qualquer código para dealloc, didReceiveMemoryWarning, viewDidUnload, ... e sua propriedade pode seguramente ser tornada pública. Se você estava preocupado com a falta de propriedades dealloc, talvez seja hora de verificar novamente seus levantadores.

Cœur
fonte