IBOutlets dovrebbe essere forte o debole sotto ARC?


551

Sto sviluppando esclusivamente per iOS 5 utilizzando ARC. Qualora IBOutletS per UIViews (e sottoclassi) sia strongo weak?

Il seguente:

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

Mi sbarazzerei di tutto questo:

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

Ci sono problemi a farlo? I modelli stanno usando strongcome sono le proprietà generate automaticamente create quando ci si collega direttamente all'intestazione dall'editor 'Interface Builder', ma perché? L' UIViewControllerha già un strongriferimento ai suoi viewche conserva la sua subviews.


11
Come nota, IBOutletCollection()non deve essere weak, altrimenti restituisce come nil.
ohho,

Xcode 8.2.1 usa debole durante la creazione di IBOutlet tramite l'interfaccia builder. Comunque molte risposte qui su SO consigliano di usare forte.
Neoneye,

1
@neoneye Ho appena provato con xcode 8.3.2 trascinando dallo storyboard al file rapido e per impostazione predefinita èstrong
CupawnTae

Risposte:


252

La corrente negativa migliore pratica da Apple è per IBOutlets siano forti meno debole è specificamente necessaria per evitare un ciclo conservare. Come Johannes ha menzionato sopra, questo è stato commentato nella sessione "Implementazione di progetti di interfaccia utente in Interface Builder" dal WWDC 2015 in cui un ingegnere Apple ha dichiarato:

E l'ultima opzione che voglio sottolineare è il tipo di archiviazione, che può essere forte o debole. In generale, dovresti rafforzare il tuo punto vendita, specialmente se stai collegando un punto vendita a una sottoview o a un vincolo che non sarà sempre mantenuto dalla gerarchia della vista. L'unica volta in cui è davvero necessario indebolire un punto vendita è se si dispone di una vista personalizzata che fa riferimento a qualcosa di backup della gerarchia della vista e in generale non è consigliabile.

Ho chiesto a questo proposito su Twitter a un ingegnere del team IB e ha confermato che il valore predefinito dovrebbe essere forte e che i documenti per gli sviluppatori vengono aggiornati.

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


33
È davvero vero o la risposta con oltre 300 voti è quella corretta? Ho notato che InterfaceBuilder per impostazione predefinita utilizza debole quando si trascina Ctrl dallo storyboard su .h
Arunabh Das

4
Quello con oltre 400 voti è corretto, ma obsoleto. Dal momento che iOS 6 viewDidUnload non viene chiamato, quindi non ci sono vantaggi per avere punti deboli.
kjam,

7
@kjam ci sono vantaggi. Innanzitutto non dovresti avere un forte riferimento a qualcosa che non hai creato. In secondo luogo, il guadagno in termini di prestazioni è trascurabile. Non violare le migliori pratiche di programmazione semplicemente perché un ragazzo, anche un ragazzo ben posizionato, ha detto che questo è 10 microsecondi più veloce. Intenzione chiara del codice, non provare a giocare ottimizzando il compilatore. Il codice solo per le prestazioni quando è stato misurato in un caso specifico costituisce un problema.
Cameron Lowell Palmer,

5
Lasciami in disaccordo con te. "Mantenere un forte riferimento a qualcosa che non hai creato" accade sempre in Objective-C. Ecco perché esiste un conteggio dei riferimenti , piuttosto che un singolo proprietario. Hai dei riferimenti per eseguire il backup di questa raccomandazione? Potresti elencare gli altri vantaggi di punti deboli?
kjam,

4
Ecco il video del WWDC menzionato nella risposta developer.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn

450

ATTENZIONE, RISPOSTA AGGIORNATA : questa risposta non è aggiornata secondo WWDC 2015, per la risposta corretta fare riferimento alla risposta accettata (Daniel Hall) sopra. Questa risposta rimarrà per la cronaca.


Riassunto dalla libreria degli sviluppatori :

Da un punto di vista pratico, in iOS e OS X le prese dovrebbero essere definite come proprietà dichiarate. I punti vendita dovrebbero essere generalmente deboli, ad eccezione di quelli dal proprietario del file agli oggetti di livello superiore in un file pennino (o, in iOS, una scena di storyboard) che dovrebbe essere forte. I punti vendita creati saranno pertanto generalmente deboli per impostazione predefinita, poiché:

  • I punti vendita creati, ad esempio, per le viste secondarie della vista di un controller di visualizzazione o della finestra di un controller di finestra, sono riferimenti arbitrari tra oggetti che non implicano la proprietà.

  • I punti di forza sono spesso specificati da classi di framework (ad esempio, punto di vista di UIViewController o punto di finestra di NSWindowController).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;

10
Come hai ottenuto il link "libreria sviluppatore" per passare alla parte particolare della pagina di Apple Doc? Ogni volta che mi collego ai documenti di Apple, si collega sempre alla parte superiore della pagina (anche se il contenuto di interesse è a metà pagina). Grazie.
bearMountain

68
Ho copiato il collegamento dal riquadro di navigazione a sinistra. : D
Alexsander Akers,

27
Che cosa significa "ad eccezione di quelli dal proprietario del file agli oggetti di livello superiore in un file pennino (o, in iOS, una scena dello storyboard)"?
Van Du Tran,

16
@VanDuTran - significa che gli oggetti nel NIB sono a livello di root, ovvero dicono che hai istanziato un'altra vista che non è direttamente una sottoview della vista principale, quindi deve avere un forte riferimento.
Mattjgalloway,

6
Il livello superiore indica che quando si osserva il pennino, l'oggetto appare nell'elenco a sinistra. Quasi tutti i pennini hanno un UIView in essi - questo potrebbe essere l'unico oggetto di livello superiore. Se aggiungi altri elementi e questi vengono visualizzati nell'elenco, sono "oggetti di livello superiore"
David H,

50

Mentre la documentazione consiglia di utilizzare le weakproprietà per le visualizzazioni secondarie, dal momento che iOS 6 sembra invece usare bene strong(il qualificatore di proprietà predefinito). Ciò è causato dalla modifica delle UIViewControllerviste che non viene più scaricata.

  • Prima di iOS 6, se si mantenevano forti collegamenti con le visualizzazioni secondarie della vista del controller in giro, se la vista principale del controller della vista veniva scaricata, quelle rimarrebbero aggrappate alle visualizzazioni secondarie fintanto che il controller della vista è in circolazione.
  • Da iOS 6, le viste non vengono più scaricate, ma caricate una volta e quindi restano in posizione finché il controller è presente. Quindi le proprietà forti non contano. Inoltre, non creano cicli di riferimento forti, poiché indicano il grafico di riferimento forte.

Detto questo, sono combattuto tra l'utilizzo

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

e

@property (nonatomic) IBOutlet UIButton *button;

in iOS 6 e versioni successive:

  • L'uso weakindica chiaramente che il controller non desidera la proprietà del pulsante.

  • Ma omettere weaknon fa male in iOS 6 senza scaricare la visualizzazione ed è più breve. Alcuni potrebbero sottolineare che è anche più veloce, ma devo ancora incontrare un'app che è troppo lenta a causa di weak IBOutlets.

  • Il mancato utilizzo weakpuò essere percepito come un errore.

In conclusione: da iOS 6 non possiamo più sbagliare finché non utilizziamo lo scaricamento della vista. Tempo di fare festa. ;)


Questo è vero, ma potresti comunque voler scaricare tu stesso la vista. Nel qual caso dovresti impostare nilmanualmente tutti i punti vendita .
hypercrypt

PS: weakè un po 'più economico in ARM64: D
hypercrypt il

Esatto, se si implementa lo scarico della vista, le weakproprietà o le __weakvariabili di istanza sono la strada da percorrere. Volevo solo sottolineare che qui c'è meno potenziale di errore. Per quanto riguarda weakessere più economico su arm64, non ho nemmeno visto un problema di prestazioni nella vita reale con weak IBOutlets su armv7. :)
Tammo Freese l'

In quel caso, strongha anche senso. strongè dannoso solo se usi lo scaricamento della vista, ma chi lo fa in questi giorni? :)
Tammo Freese,

2
@Rocotilos Il primo iPhone aveva una RAM molto limitata. Se ricordo bene, 128 MB, lasciando circa 10 MB per l'app attiva. Avere un piccolo footprint di memoria era cruciale, quindi c'era lo scarico della vista. Ciò è cambiato poiché ora abbiamo sempre più RAM e Apple ha ottimizzato le UIVview in iOS 6, in modo che sugli avvisi di memoria sia possibile liberare molta memoria senza scaricare la vista.
Tammo Freese,

34

Non vedo alcun problema con quello. Pre-ARC, ho sempre realizzato i miei IBOutlet assign, poiché sono già conservati dalle loro superview. Se li fai weak, non dovresti annullarli in viewDidUnload, come fai notare.

Un avvertimento: puoi supportare iOS 4.x in un progetto ARC, ma se lo fai, non puoi usarlo weak, quindi dovresti crearli assign, nel qual caso vorrai comunque annullare il riferimento viewDidUnloadper evitare un puntatore penzolante. Ecco un esempio di un bug puntatore penzolante che ho riscontrato:

Un UIViewController ha un UITextField per il codice postale. Utilizza CLLocationManager per invertire il codice geografico della posizione dell'utente e impostare il codice postale. Ecco il callback delegato:

-(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];
}

Ho scoperto che se avessi rifiutato questa visione al momento giusto e non avessi annullato self.zip viewDidUnload, il callback del delegato avrebbe potuto generare un'eccezione di accesso errato su self.zip.text.


4
Comprendo anche che weaknon è necessario inserire le proprietà viewDidUnload. Ma perché il modello di Apple per la creazione di punti vendita include un [self setMySubview:nil]?
Yang Meyer,

3
Esistono casi nel mondo reale in cui l'utilizzo di strong / conservato per IBOutlet potrebbe causare problemi? O è solo una conservazione ridondante, il che significa cattivo stile di codifica ma non influirebbe sul tuo codice?
Enzo Tran,

1
Esiste una conservazione ridondante? Se è presente un valore di conservazione aggiuntivo, ciò non consentirà di conteggiarlo correttamente e pertanto non verrà liberato non appena potrebbe essere poiché è presente un valore di conservazione aggiuntivo nel relativo conteggio.
karlbecker_com,

25

IBOutletdovrebbe essere forte, per motivi di prestazioni. Vedi Storyboard Reference, Strong IBOutlet, Scene Dock in iOS 9

Come spiegato in questo paragrafo, gli sbocchi per le visualizzazioni secondarie della vista del controller di visualizzazione possono essere deboli, poiché tali visualizzazioni secondarie sono già di proprietà dell'oggetto di livello superiore del file pennino. Tuttavia, quando un Outlet è definito come un puntatore debole e il puntatore è impostato, ARC chiama la funzione di runtime:

id objc_storeWeak(id *object, id value);

Ciò aggiunge il puntatore (oggetto) a una tabella usando il valore dell'oggetto come chiave. Questa tabella viene definita tabella debole. ARC utilizza questa tabella per archiviare tutti i puntatori deboli dell'applicazione. Ora, quando il valore dell'oggetto è deallocato, ARC ripeterà la tabella debole e imposterà il riferimento debole su zero. In alternativa, ARC può chiamare:

void objc_destroyWeak(id * object)

Quindi, l'oggetto non è registrato e objc_destroyWeak chiama di nuovo:

objc_storeWeak(id *object, nil)

Questa tenuta della contabilità associata a un riferimento debole può richiedere 2-3 volte più a lungo rispetto al rilascio di un riferimento forte. Quindi, un riferimento debole introduce un sovraccarico per il runtime che è possibile evitare semplicemente definendo punti vendita come forti.

A partire da Xcode 7, suggerisce strong

Se guardi la sessione 407 del WWDC 2015 Implementare i disegni dell'interfaccia utente in Interface Builder , suggerisce (trascrizione da http://asciiwwdc.com/2015/sessions/407 )

E l'ultima opzione che voglio sottolineare è il tipo di archiviazione, che può essere forte o debole.

In generale, dovresti rafforzare il tuo outlet, specialmente se stai collegando un outlet a una vista secondaria o a un vincolo che non sarà sempre mantenuto dalla gerarchia della vista.

L'unica volta in cui è davvero necessario indebolire un punto vendita è se si dispone di una vista personalizzata che fa riferimento a qualcosa di backup della gerarchia della vista e in generale non è consigliabile.

Quindi sceglierò forte e farò clic su Connetti che genererà il mio punto vendita.


1
Ottima risposta che spiega il vero motivo -perché-
micnguyen

Questo è buono e tutti, ma ho visto perdite provenienti da riconoscitori di gesti implementati nello storyboard.
thibaut noah,

1
Non riesco a capire questa linea. "L'unica volta in cui hai davvero bisogno di indebolire uno sbocco è se hai una vista personalizzata che fa riferimento a qualcosa di backup nella gerarchia della vista e in generale non è consigliabile." Qualche esempio?
user1872384

Ho calcolato il tempo deinit che impiega debole e forte, ed è esattamente lo stesso.
touti,

Ma in questo è più rapido. I riferimenti deboli sono più veloci.
thesummersign

20

Nello sviluppo iOS il caricamento NIB è leggermente diverso dallo sviluppo Mac.

Nello sviluppo di Mac, un IBOutlet è di solito un riferimento debole: se si dispone di una sottoclasse di NSViewController, viene conservata solo la vista di livello superiore e quando si distribuisce il controller, tutte le sue visualizzazioni secondarie e punti vendita vengono liberati automaticamente.

UiViewController utilizza Key Value Coding per impostare i punti vendita utilizzando riferimenti forti. Pertanto, quando si distribuisce UIViewController, la vista superiore viene automaticamente deallocata, ma è necessario anche deallocare tutti i suoi punti vendita nel metodo dealloc.

In questo post del Big Nerd Ranch , trattano questo argomento e spiegano anche perché usare un forte riferimento in IBOutlet non è una buona scelta (anche se è raccomandato da Apple in questo caso).


16
Lo spiega al 2009. Con ARC, questo è cambiato in modo significativo.
Dafydd Williams,

1
:( il link al Big Nerd Ranch è morto ... eppure ho davvero bisogno di leggerlo. Qualcuno sa maggiori dettagli su quel post, quindi posso trovarlo?
Motti Shneor,

@MottiShneor non ti preoccupare, non è un grosso problema dal momento che il link era circa volte prima di ARC e non è più rilevante.
Sergey Grischyov,

18

Una cosa che vorrei sottolineare qui, e cioè, nonostante ciò che gli ingegneri Apple hanno dichiarato nel loro video del WWDC 2015 qui:

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

Apple continua a cambiare idea sull'argomento, il che ci dice che non esiste una sola risposta corretta a questa domanda. Per dimostrare che anche gli ingegneri Apple sono divisi su questo argomento, dai un'occhiata al codice di esempio più recente di Apple e vedrai alcune persone usare deboli, altre no.

In questo esempio di Apple Pay si usa debole: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController

Come fa questo esempio picture-in-picture: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP4001616PP_Viewer

Come nell'esempio Lister: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Come nell'esempio Core Location: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_sinkiftDont

Come fa l'anteprima del controller di visualizzazione: https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html_//elpre_view_pre_view

Come nel caso dell'esempio HomeKit: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/DS_48_TP001

Tutti questi sono completamente aggiornati per iOS 9 e tutti usano punti deboli. Da ciò apprendiamo che A. Il problema non è così semplice come alcune persone pensano che sia. B. Apple ha cambiato idea più volte e C. Puoi usare tutto ciò che ti rende felice :)

Un ringraziamento speciale a Paul Hudson (autore di www.hackingwithsift.com) che mi ha dato il chiarimento e i riferimenti per questa risposta.

Spero che questo chiarisca un po 'meglio l'argomento!

Stai attento.


Ho verificato questo problema da qualche tempo e non ho trovato risposte concrete. Dal momento che il link sopra suggerisce che entrambi vanno bene e in generale vanno con ciò che Xcode autosuggesta.
subin272


6

Essere consapevoli, IBOutletCollectiondovrebbe essere @property (strong, nonatomic).


3
Perché non copycome è un NSArray?
hypercrypt

5

Sembra che qualcosa sia cambiato nel corso degli anni e ora Apple consiglia di utilizzare forte in generale. Le prove sulla loro sessione del WWDC si trovano nella sessione 407 - Implementazione dei disegni dell'interfaccia utente in Interface Builder e inizia alle 32:30. La mia nota da ciò che dice è (quasi, se non esattamente, citandolo):

  • le connessioni outlet in generale dovrebbero essere forti soprattutto se connettiamo una sottoview o un vincolo che non viene sempre mantenuto dalla gerarchia della vista

  • potrebbe essere necessaria una connessione con presa debole quando si creano viste personalizzate che hanno qualche riferimento a qualcosa di backup nella gerarchia delle viste e in generale non è raccomandato

In altri reparti dovrebbe essere sempre forte ora, purché alcune delle nostre visualizzazioni personalizzate non creino un ciclo di conservazione con parte della vista in alto nella gerarchia delle viste

MODIFICARE :

Alcuni potrebbero porre la domanda. Mantenerlo con un riferimento forte non crea un ciclo di mantenimento come controller della vista principale e la vista proprietaria mantiene il riferimento ad esso? O perché quel cambiamento è successo? Penso che la risposta sia precedente in questo discorso quando descrivono come i pennini vengono creati dallo xib. È stato creato un pennino separato per un VC e per la vista. Penso che questo potrebbe essere il motivo per cui cambiano le raccomandazioni. Sarebbe comunque bello avere una spiegazione più approfondita da parte di Apple.


4

Penso che le informazioni più importanti siano: gli elementi in xib sono automaticamente nelle sottoview della vista. Sottoviste è NSArray. NSArray possiede i suoi elementi. ecc. hanno puntatori forti su di essi. Quindi nella maggior parte dei casi non si desidera creare un altro puntatore forte (IBOutlet)

E con ARC non devi fare nulla viewDidUnload

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.