O IBOutlets deve ser forte ou fraco no ARC?

551

Estou desenvolvendo exclusivamente para o iOS 5 usando o ARC. Should IBOutlets para UIViews (e subclasses) ser strongou weak?

Os seguintes:

@property (nonatomic, weak) IBOutlet UIButton *button;

Se livraria de tudo isso:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

Existem problemas para fazer isso? Os modelos estão usando strongas propriedades geradas automaticamente, criadas ao se conectar diretamente ao cabeçalho no editor 'Interface Builder', mas por quê? O UIViewControllerjá possui uma strongreferência ao viewque retém suas subvisões.

hypercrypt
fonte
11
Como uma nota, IBOutletCollection()não deve ser weak, caso contrário, ele retornará como nil.
ohho 31/07
O Xcode 8.2.1 usa fraco ao criar IBOutlets via construtor de interface. No entanto, muitas respostas aqui no SO recomendam usar forte.
23417 neoneye
1
@neoneye Eu apenas tentei com o Xcode 8.3.2 arrastar de storyboard para um arquivo rápida e o padrão éstrong
CupawnTae

Respostas:

252

A corrente recomendado melhores práticas da Apple é para IBOutlets a ser forte , a menos fraco é especificamente necessário para evitar a reter ciclo. Como Johannes mencionou acima, isso foi comentado na sessão "Implementando projetos de interface do usuário no Interface Builder" da WWDC 2015, onde um engenheiro da Apple disse:

E a última opção que quero destacar é o tipo de armazenamento, que pode ser forte ou fraco. Em geral, você deve fortalecer sua tomada, especialmente se estiver conectando uma tomada a uma subvisão ou a uma restrição que nem sempre será mantida pela hierarquia de exibições. O único momento em que você realmente precisa tornar uma saída fraca é se você tiver uma exibição personalizada que faça referência a algo que faça backup da hierarquia da exibição e, em geral, isso não seja recomendado.

Perguntei sobre isso no Twitter a um engenheiro da equipe de IB e ele confirmou que forte deve ser o padrão e que os documentos do desenvolvedor estão sendo atualizados.

https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104

Daniel Hall
fonte
33
Isso é realmente verdade ou a resposta com mais de 300 votos positivos é a correta? Notei que InterfaceBuilder por padrão usa fraco quando Ctrl-arrastar a partir do storyboard para a .h
Arunabh Das
4
Aquele com mais de 400 votos está correto, mas desatualizado. Como o iOS 6 viewDidUnload não é chamado, não há benefícios em ter pontos de venda fracos.
kjam
7
@kjam existem benefícios. Em primeiro lugar, você não deve ter uma forte referência a algo que não criou. Segundo, o ganho de desempenho é insignificante. Não viole as melhores práticas de programação simplesmente porque um cara, mesmo um cara bem colocado, disse que isso é 10 microssegundos mais rápido. Código de intenção clara, não tente reproduzir o compilador de otimização. Codifique apenas o desempenho quando tiver sido medido em um caso específico como um problema.
Cameron Lowell Palmer
5
Deixe-me discordar de você. 'Manter uma forte referência a algo que você não criou' acontece o tempo todo no Objective-C. É por isso que existe uma contagem de referência , e não um único proprietário. Você tem alguma referência para fazer backup desta recomendação? Você poderia listar os outros benefícios de pontos fracos?
kjam
4
Aqui está o vídeo da WWDC mencionado na resposta developer.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn
450

AVISO, RESPOSTA ATUALIZADA : esta resposta não está atualizada conforme WWDC 2015, para obter a resposta correta, consulte a resposta aceita (Daniel Hall) acima. Esta resposta ficará registrada.


Resumido da biblioteca do desenvolvedor :

De uma perspectiva prática, no iOS e no OS X, as tomadas devem ser definidas como propriedades declaradas. As tomadas geralmente devem ser fracas, exceto aquelas do Proprietário do arquivo aos objetos de nível superior em um arquivo de ponta (ou, no iOS, uma cena de storyboard) que deve ser forte. As saídas que você cria, portanto, geralmente são fracas por padrão, porque:

  • As saídas criadas para, por exemplo, sub-visões da visualização de um controlador de exibição ou da janela de um controlador de janela, são referências arbitrárias entre objetos que não implicam propriedade.

  • As tomadas fortes são frequentemente especificadas pelas classes de estrutura (por exemplo, saída de visualização do UIViewController ou saída de janela do NSWindowController).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
Alexsander Akers
fonte
10
Como você conseguiu o link "biblioteca do desenvolvedor" para ir para a parte específica da página de documento da apple? Sempre que eu vinculo aos documentos da apple, ele sempre é vinculado à parte superior da página (mesmo que o conteúdo de interesse esteja na metade da página). Obrigado.
amigos estão dizendo sobre clínica de reabilitação
68
Copiei o link no painel de navegação à esquerda. : D
Alexsander Akers
27
O que significa "exceto aqueles do proprietário do arquivo aos objetos de nível superior em um arquivo de ponta (ou, no iOS, uma cena de storyboard)"?
Van Du Tran
16
@VanDuTran - significa objetos no NIB que estão no nível raiz, ou seja, digamos que você instanciou outra visão lá que não é diretamente uma sub-visão da visão principal, então ela precisa ter uma referência forte.
27512 Mattjgalloway
6
Nível superior significa que, quando você olha para a ponta, o objeto aparece na lista à esquerda. Quase todas as pontas possuem uma UIView - esse pode ser o único objeto de nível superior. Se você adicionar outros itens, e eles mostram na lista, eles são "objetos de alto nível"
David H
50

Embora a documentação recomende o uso weakde propriedades para sub-visualizações, desde o iOS 6 parece bom usá-lo strong(o qualificador de propriedade padrão). Isso é causado pela mudança em UIViewControllerque as visualizações não são mais descarregadas.

  • Antes do iOS 6, se você mantivesse vínculos fortes com as sub-visões da visualização do controlador, se a visualização principal do controlador fosse descarregada, elas permaneceriam nas sub-visualizações enquanto o controlador de visualização estivesse presente.
  • Desde o iOS 6, as visualizações não são mais descarregadas, mas carregadas uma vez e permanecem enquanto o controlador estiver lá. Portanto, propriedades fortes não importam. Eles também não criarão ciclos de referência fortes, pois apontam o gráfico de referência forte.

Dito isto, estou dividido entre usar

@property (nonatomic, weak) IBOutlet UIButton *button;

e

@property (nonatomic) IBOutlet UIButton *button;

no iOS 6 e posterior:

  • O uso weakindica claramente que o controlador não deseja a propriedade do botão.

  • Mas omitir weaknão faz mal no iOS 6 sem descarregar a visualização e é mais curto. Alguns podem apontar que também é mais rápido, mas ainda não encontrei um aplicativo muito lento por causa de weak IBOutlets.

  • Não usar weakpode ser percebido como um erro.

Conclusão: desde o iOS 6, não podemos mais entender errado desde que não utilizemos o descarregamento de visualizações. Hora de festejar. ;)

Tammo Freese
fonte
Isso é verdade, mas você ainda pode querer descarregar a exibição por conta própria. Nesse caso, você teria que definir todas as suas saídas nilmanualmente.
hypercrypt
PS: weaké um pouco mais barato no ARM64: D
hypercrypt
É isso mesmo, se você implementar o descarregamento de exibição, weakpropriedades ou __weakvariáveis ​​de instância são o caminho a percorrer. Eu só queria ressaltar que há menos potencial de erro aqui. Quanto a weakser mais barato no arm64, eu nem vi um problema de desempenho na vida real com weak IBOutlets no armv7. :)
Tammo Freese
Nesse caso, strongfaz sentido também. strongsó é prejudicial se você usar o descarregamento de visualizações - mas quem usa atualmente? :)
Tammo Freese 26/03
2
@Rocotilos O primeiro iPhone tinha RAM muito limitada. Se bem me lembro, 128 MB, deixando cerca de 10 MB para o aplicativo ativo. Ter uma pequena pegada de memória era crucial, portanto, havia o descarregamento de visualizações. Isso mudou, pois agora temos mais e mais RAM, e a Apple otimizou UIViews no iOS 6, para que, nos avisos de memória, muita memória possa ser liberada sem descarregar a exibição.
Tammo Freese 04/04
34

Não vejo nenhum problema com isso. Antes do ARC, eu sempre fiz meus IBOutlets assign, pois eles já são mantidos por suas superviews. Se você as criar weak, não precisará defini-las em viewDidUnload, como você indica.

Uma ressalva: você pode oferecer suporte ao iOS 4.x em um projeto ARC, mas se o fizer, não poderá usá- weaklo; portanto, será necessário fazê-lo assign; nesse caso, você ainda deseja anular a referência viewDidUnloadpara evitar um ponteiro pendente. Aqui está um exemplo de um bug de ponteiro danificado que eu experimentei:

Um UIViewController possui um UITextField para CEP. Ele usa CLLocationManager para inverter o código geográfico da localização do usuário e definir o CEP. Aqui está o retorno de chamada do delegado:

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation {
    Class geocoderClass = NSClassFromString(@"CLGeocoder");
    if (geocoderClass && IsEmpty(self.zip.text)) {
        id geocoder = [[geocoderClass alloc] init];
        [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
            if (self.zip && IsEmpty(self.zip.text)) {
                self.zip.text = [[placemarks objectAtIndex:0] postalCode];
            }
        }];    
    }
    [self.locationManager stopUpdatingLocation];
}

Descobri que se eu rejeitasse essa visualização no momento certo e não fizesse o self.zip viewDidUnload, o retorno de chamada delegado poderia gerar uma exceção de acesso ruim no self.zip.text.

Christopher Pickslay
fonte
4
Também entendo que as weakpropriedades não precisam ser detalhadas viewDidUnload. Mas por que o modelo da Apple para criação de pontos de venda inclui um [self setMySubview:nil]?
Yang Meyer
3
Existe algum caso no mundo real em que o uso forte / retido para o seu IBOutlet possa causar problemas? Ou é apenas uma retenção redundante, o que significa um estilo de codificação incorreto, mas não afetaria seu código?
Enzo Tran
1
Existe uma retenção redundante? Se houver uma retenção extra, isso fará com que ela não seja contada adequadamente e, portanto, não será liberada assim que possível, pois há uma retenção extra na contagem de retenção.
22414 karlecker_com
25

IBOutletdeve ser forte, por razões de desempenho. Consulte Referência do Storyboard, IBOutlet forte, Base de cena no iOS 9

Conforme explicado neste parágrafo, as saídas para as subviews da visualização do controlador de exibição podem ser fracas, porque essas subviews já pertencem ao objeto de nível superior do arquivo nib. No entanto, quando uma saída é definida como um ponteiro fraco e o ponteiro é definido, o ARC chama a função de tempo de execução:

id objc_storeWeak(id *object, id value);

Isso adiciona o ponteiro (objeto) a uma tabela usando o valor do objeto como chave. Essa tabela é chamada de tabela fraca. O ARC usa esta tabela para armazenar todos os indicadores fracos do seu aplicativo. Agora, quando o valor do objeto for desalocado, o ARC iterará sobre a tabela fraca e definirá a referência fraca como nulo. Como alternativa, o ARC pode chamar:

void objc_destroyWeak(id * object)

Em seguida, o objeto não é registrado e objc_destroyWeak chama novamente:

objc_storeWeak(id *object, nil)

Essa contabilidade associada a uma referência fraca pode levar de 2 a 3 vezes mais do que a liberação de uma referência forte. Portanto, uma referência fraca introduz uma sobrecarga para o tempo de execução que você pode evitar, simplesmente definindo saídas como fortes.

A partir do Xcode 7, sugere strong

Se você assistir a sessão 407 da WWDC 2015 Implementando designs de interface do usuário no Interface Builder , sugere (transcrição de http://asciiwwdc.com/2015/sessions/407 )

E a última opção que quero destacar é o tipo de armazenamento, que pode ser forte ou fraco.

Em geral, você deve fortalecer sua tomada, especialmente se estiver conectando uma tomada a uma subvisualização ou a uma restrição que nem sempre será mantida pela hierarquia da visualização.

O único momento em que você realmente precisa tornar uma saída fraca é se você tiver uma exibição personalizada que faça referência a algo que faça backup da hierarquia da exibição e, em geral, isso não seja recomendado.

Então, eu vou escolher forte e clicarei em conectar, o que gerará minha saída.

onmyway133
fonte
1
Grande resposta que explica o motivo real -why-
micnguyen
Isso é bom e tudo, mas vi vazamentos provenientes de reconhecedores de gestos implementados no storyboard.
Thibaut noah
1
Eu não consigo entender essa linha. "O único momento em que você realmente precisa tornar uma saída fraca é se você tiver uma visualização personalizada que faça referência a algo que faça backup da hierarquia de visualizações e, em geral, isso não seja recomendado". Algum exemplo?
user1872384
Calculei o tempo de início que o fraco e o forte levam, e é exatamente o mesmo.
touti 18/07/19
Mas rapidamente, esse é mais o caso. Referências fracas são mais rápidas.
Thesummersign
20

No desenvolvimento iOS, o carregamento do NIB é um pouco diferente do desenvolvimento do Mac.

No desenvolvimento para Mac, um IBOutlet geralmente é uma referência fraca: se você tiver uma subclasse do NSViewController, apenas a visualização de nível superior será mantida e quando você desalocar o controlador, todas as subvisões e saídas serão liberadas automaticamente.

O UiViewController usa a codificação de valores-chave para definir as saídas usando referências fortes. Portanto, quando você desalocar seu UIViewController, a vista superior será desalocada automaticamente, mas você também deve desalocar todas as suas saídas no método desalocação.

Nesta postagem do Big Nerd Ranch , eles abordam este tópico e também explicam por que o uso de uma referência forte no IBOutlet não é uma boa opção (mesmo se recomendado pela Apple neste caso).

Giuseppe
fonte
16
Ele explica isso em 2009. Com o ARC, isso mudou significativamente.
Dafydd Williams
1
:( o link Big Nerd Ranch está morto ... mas eu realmente preciso lê-lo Qualquer um sabe mais detalhes sobre esse post, para que eu possa encontrá-lo.?
Motti Shneor
@MottiShneor não se preocupe, não é grande coisa, já que o link estava muito antes do ARC e não é mais relevante.
Sergey Grischyov 11/08/14
18

Uma coisa que gostaria de salientar aqui, e isso é, apesar do que os engenheiros da Apple declararam em seu próprio vídeo da WWDC 2015 aqui:

https://developer.apple.com/videos/play/wwdc2015/407/

A Apple continua mudando de idéia sobre o assunto, o que nos diz que não existe uma resposta certa para essa pergunta. Para mostrar que até os engenheiros da Apple estão divididos sobre esse assunto, dê uma olhada no código de exemplo mais recente da Apple e você verá que algumas pessoas usam pouco e outras não.

Este exemplo do Apple Pay usa fraco: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_swift-DontLinkElementID

Como este exemplo imagem a imagem: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_Plift_Player_Player_Player_Player_Player_Player

Assim como o exemplo Lister: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Assim como o exemplo do Local Principal: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swift-DontLinkElement

Como o exemplo de visualização do controlador de exibição: https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_Details

Da mesma forma que o exemplo do HomeKit: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP40015048-HMCatalog_Homes_ID23

Todos esses são totalmente atualizados para o iOS 9 e todos usam pontos fracos. A partir disso, aprendemos que A. A questão não é tão simples como algumas pessoas pensam ser. B. A Apple mudou de idéia repetidamente, e C. Você pode usar o que quer que te faça feliz :)

Agradecimentos especiais a Paul Hudson (autor de www.hackingwithsift.com), que me deu os esclarecimentos e as referências para esta resposta.

Espero que isso esclareça um pouco melhor o assunto!

Cuidar.

syedfa
fonte
Eu estive checando esse problema há algum tempo e não encontrei respostas concretas. Como o link acima sugere que ambos estão bem e, em geral, seguem o que o Xcode sugere automaticamente.
Subin272
9

Na WWDC 2015, há uma sessão sobre Implementando designs de interface do usuário no Interface Builder . Em torno da marca de 32 minutos, ele diz que você sempre quer se @IBOutlet fortalecer .

Johannes
fonte
Interessante. Eu acho que isso mudou quando o descarregamento de exibição foi removido?
hypercrypt
6

Esteja ciente, IBOutletCollectiondeveria ser @property (strong, nonatomic).

landonandrey
fonte
3
Por que não copycomo é um NSArray?
hypercrypt
5

Parece que algo mudou ao longo dos anos e agora a Apple recomenda usar forte em geral. A evidência em sua sessão do WWDC está na sessão 407 - Implementando designs de interface do usuário no Interface Builder e começa às 32:30. Minha nota do que ele diz é (quase, se não exatamente, citando-o):

  • as conexões de tomada em geral devem ser fortes, especialmente se conectarmos uma subvisão ou restrição que nem sempre é mantida pela hierarquia da visualização

  • pode ser necessária uma conexão de saída fraca ao criar visualizações personalizadas que tenham alguma referência a algo de backup na hierarquia de visualizações e, em geral, não é recomendado

Em outras alas, deve ser sempre forte agora, desde que algumas de nossas visualizações personalizadas não criem um ciclo de retenção com algumas das visualizações na hierarquia de visualizações

EDIT:

Alguns podem fazer a pergunta. Mantê-lo com uma referência forte não cria um ciclo de retenção como o controlador de visualização raiz e a visualização proprietária mantém a referência a ele? Ou por que isso mudou aconteceu? Eu acho que a resposta está no início desta conversa, quando eles descrevem como as pontas são criadas a partir da xib. Existe uma ponta separada criada para um VC e para a exibição. Eu acho que essa pode ser a razão pela qual eles mudam as recomendações. Ainda assim, seria bom obter uma explicação mais profunda da Apple.

Julian Król
fonte
4

Eu acho que a informação mais importante é: Os elementos no xib são automaticamente em sub-visões de exibição. Subviews é NSArray. O NSArray possui seus elementos. etc têm fortes indicadores sobre eles. Portanto, na maioria dos casos, você não deseja criar outro ponteiro forte (IBOutlet)

E com o ARC, você não precisa fazer nada viewDidUnload

kraag22
fonte