Sollten IBOutlets unter ARC stark oder schwach sein?

551

Ich entwickle ausschließlich für iOS 5 mit ARC. Sollte IBOutlets bis UIViews (und Unterklassen) sein strongoder weak?

Folgende:

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

Würde all das loswerden:

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

Gibt es dabei Probleme? Die Vorlagen werden ebenso verwendet strongwie die automatisch generierten Eigenschaften, die beim direkten Herstellen einer Verbindung zum Header aus dem Editor "Interface Builder" erstellt werden. Aber warum? Das hat UIViewControllerbereits einen strongVerweis auf sein, viewder seine Unteransichten behält.

hypercrypt
quelle
11
Als Hinweis IBOutletCollection()darf nicht sein weak, sonst gibt es als zurück nil.
Ohho
Xcode 8.2.1 verwendet beim Erstellen von IBOutlets über den Interface Builder eine schwache Funktion. Allerdings raten viele Antworten hier auf SO, stark zu verwenden.
Neoneye
1
@neoneye Ich habe gerade versucht, mit xcode 8.3.2 vom Storyboard in eine schnelle Datei zu ziehen, und es ist standardmäßigstrong
CupawnTae

Antworten:

252

Die derzeit von Apple empfohlene Best Practice lautet, dass IBOutlets stark sind, es sei denn, es ist speziell eine Schwäche erforderlich, um einen Aufbewahrungszyklus zu vermeiden. Wie Johannes oben erwähnt hat, wurde dies in der Sitzung "Implementieren von UI-Designs in Interface Builder" von WWDC 2015 kommentiert, in der ein Apple Engineer sagte:

Und die letzte Option, auf die ich hinweisen möchte, ist der Speichertyp, der entweder stark oder schwach sein kann. Im Allgemeinen sollten Sie Ihren Ausgang stark machen, insbesondere wenn Sie einen Ausgang mit einer Unteransicht oder einer Einschränkung verbinden, die nicht immer von der Ansichtshierarchie beibehalten wird. Das einzige Mal, wenn Sie eine Steckdose wirklich schwach machen müssen, ist, wenn Sie eine benutzerdefinierte Ansicht haben, die auf etwas verweist, das die Ansichtshierarchie sichert, und dies wird im Allgemeinen nicht empfohlen.

Ich habe auf Twitter einen Ingenieur im IB-Team danach gefragt und er hat bestätigt, dass stark die Standardeinstellung sein sollte und dass die Entwicklerdokumente aktualisiert werden.

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

Daniel Hall
quelle
33
Ist das wirklich wahr oder ist die Antwort mit mehr als 300 Upvotes die richtige? Ich habe festgestellt, dass InterfaceBuilder standardmäßig schwach verwendet, wenn Sie bei gedrückter Strg-Taste vom Storyboard auf .h
Arunabh Das
4
Der mit mehr als 400 Stimmen ist korrekt, aber veraltet. Da iOS 6 viewDidUnload nicht aufgerufen wird, gibt es keine Vorteile für schwache Steckdosen.
KJAM
7
@kjam gibt es Vorteile. In erster Linie sollten Sie keinen starken Bezug zu etwas haben, das Sie nicht erstellt haben. Zweitens ist der Leistungsgewinn vernachlässigbar. Verstoßen Sie nicht gegen bewährte Methoden beim Programmieren, nur weil ein Typ, selbst ein gut platzierter Typ, sagte, dies sei 10 Mikrosekunden schneller. Versuchen Sie nicht, den optimierenden Compiler zu spielen. Nur Code für die Leistung, wenn er in einem bestimmten Fall als Problem gemessen wurde.
Cameron Lowell Palmer
5
Lass mich dir nicht zustimmen. In Objective-C wird immer wieder ein starker Verweis auf etwas gespeichert, das Sie nicht erstellt haben. Deshalb gibt es eine Referenzzählung , eher dann ein einzelner Eigentümer. Haben Sie Referenzen, um diese Empfehlung zu sichern? Könnten Sie bitte die anderen Vorteile schwacher Verkaufsstellen auflisten?
kjam
4
Hier ist das WWDC-Video, das in der Antwort developer.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn
450

WARNUNG, veraltete Antwort: Diese Antwort ist gemäß WWDC 2015 nicht aktuell. Die richtige Antwort finden Sie in der oben akzeptierten Antwort (Daniel Hall). Diese Antwort wird aufgezeichnet.


Aus der Entwicklerbibliothek zusammengefasst :

Aus praktischer Sicht sollten Outlets in iOS und OS X als deklarierte Eigenschaften definiert werden. Outlets sollten im Allgemeinen schwach sein, mit Ausnahme derjenigen von File's Owner bis zu Objekten der obersten Ebene in einer Nib-Datei (oder in iOS einer Storyboard-Szene), die stark sein sollten. Von Ihnen erstellte Outlets sind daher normalerweise standardmäßig schwach, da:

  • Outlets, die Sie beispielsweise für Unteransichten der Ansicht eines Ansichtscontrollers oder des Fensters eines Fenstercontrollers erstellen, sind willkürliche Verweise zwischen Objekten, die keinen Besitz implizieren.

  • Die starken Ausgänge werden häufig von Framework-Klassen angegeben (z. B. der Ansichtsausgang von UIViewController oder der Fensterausgang von NSWindowController).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    
Alexsander Akers
quelle
10
Wie haben Sie den Link "Entwicklerbibliothek" dazu gebracht, zu einem bestimmten Teil der Apple Doc-Seite zu springen? Wenn ich auf die Apple-Dokumente verweise, wird immer auf den oberen Rand der Seite verwiesen (auch wenn sich der interessierende Inhalt auf der Hälfte der Seite befindet). Vielen Dank.
BearMountain
68
Ich habe den Link aus dem Navigationsbereich links kopiert. : D
Alexsander Akers
27
Was bedeutet "außer denen vom Dateibesitzer bis zu Objekten der obersten Ebene in einer NIB-Datei (oder in iOS einer Storyboard-Szene)"?
Van Du Tran
16
@VanDuTran - bedeutet, dass sich Objekte in der NIB auf der Stammebene befinden, dh, Sie haben dort eine andere Ansicht instanziiert, die nicht direkt eine Unteransicht der Hauptansicht ist. Dann muss eine starke Referenz vorhanden sein.
Mattjgalloway
6
Oberste Ebene bedeutet, dass beim Betrachten der Feder das Objekt in der Liste links angezeigt wird. Fast alle Schreibfedern enthalten eine UIView - dies ist möglicherweise das einzige Objekt der obersten Ebene. Wenn Sie andere Elemente hinzufügen und diese in der Liste angezeigt werden, handelt es sich um "Objekte der obersten Ebene"
David H,
50

In der Dokumentation wird zwar empfohlen, weakEigenschaften für Unteransichten zu verwenden, doch scheint es seit iOS 6 in Ordnung zu sein, strongstattdessen (das Standard-Eigentumsqualifikationsmerkmal) zu verwenden. Dies liegt daran, UIViewControllerdass die Ansichten nicht mehr entladen werden.

  • Wenn Sie vor iOS 6 starke Links zu Unteransichten der Ansicht des Controllers beibehalten haben und die Hauptansicht des Ansichtscontrollers entladen wurde, bleiben diese in den Unteransichten erhalten, solange sich der Ansichtscontroller in der Nähe befindet.
  • Seit iOS 6 werden Ansichten nicht mehr entladen, sondern einmal geladen und bleiben dann so lange bestehen, wie ihr Controller vorhanden ist. Starke Eigenschaften spielen also keine Rolle. Sie erstellen auch keine starken Referenzzyklen, da sie auf das starke Referenzdiagramm zeigen.

Das heißt, ich bin zwischen dem Gebrauch hin und her gerissen

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

und

@property (nonatomic) IBOutlet UIButton *button;

in iOS 6 und danach:

  • Wenn Sie weakeindeutig angeben, möchte der Controller nicht, dass die Schaltfläche in Besitz genommen wird.

  • Das Weglassen weakschadet in iOS 6 jedoch nicht ohne das Entladen der Ansicht und ist kürzer. Einige mögen darauf hinweisen, dass dies auch schneller ist, aber ich habe noch keine App gefunden, die wegen weak IBOutlets zu langsam ist .

  • Nichtbenutzung weakkann als Fehler empfunden werden.

Fazit: Seit iOS 6 können wir das nicht mehr falsch verstehen, solange wir das Entladen von Ansichten nicht verwenden. Zeit für Party. ;)

Tammo Freese
quelle
Das stimmt, aber Sie möchten die Ansicht möglicherweise trotzdem selbst entladen. In diesem Fall müssten Sie alle Steckdosen nilmanuell einstellen .
Hypercrypt
PS: weakist ein bisschen billiger in ARM64: D
Hypercrypt
Wenn Sie das Entladen von Ansichten implementieren, sind weakEigenschaften oder __weakInstanzvariablen der richtige Weg. Ich wollte nur darauf hinweisen, dass hier weniger Fehlerpotential besteht. Da weakich auf arm64 billiger bin, habe ich mit weak IBOutlets auf armv7 noch nicht einmal ein echtes Leistungsproblem gesehen. :)
Tammo Freese
In diesem Fall strongmacht auch Sinn. strongist nur schädlich, wenn Sie das Entladen von Ansichten verwenden - aber wer macht das heutzutage? :)
Tammo Freese
2
@Rocotilos Das erste iPhone hatte sehr begrenzten RAM. Wenn ich mich richtig erinnere, 128 MB, so dass ungefähr 10 MB für die aktive App übrig bleiben. Ein kleiner Speicherbedarf war entscheidend, daher wurde die Ansicht entladen. Das hat sich geändert, da wir jetzt immer mehr RAM haben und Apple UIViews in iOS 6 optimiert hat, sodass bei Speicherwarnungen viel Speicher freigegeben werden kann, ohne die Ansicht zu entladen.
Tammo Freese
34

Damit sehe ich kein Problem. Vor ARC habe ich meine IBOutlets immer erstellt assign, da sie bereits von ihren Superviews beibehalten werden. Wenn Sie sie weakerstellen, sollten Sie sie in viewDidUnload nicht ausschließen müssen, wie Sie betonen.

Eine Einschränkung: Sie können iOS 4.x in einem ARC-Projekt unterstützen, aber wenn Sie dies tun, können Sie es nicht verwenden weak, sodass Sie sie erstellen müssen assign. In diesem Fall möchten Sie die Referenz trotzdem löschen viewDidUnload, um dies zu vermeiden ein baumelnder Zeiger. Hier ist ein Beispiel für einen baumelnden Zeigerfehler, den ich erlebt habe:

Ein UIViewController verfügt über ein UITextField für die Postleitzahl. Mit CLLocationManager wird der Standort des Benutzers geocodiert und die Postleitzahl festgelegt. Hier ist der Rückruf der Delegierten:

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

Ich stellte fest, dass viewDidUnloadder Delegaten-Rückruf eine Ausnahme für den fehlerhaften Zugriff auf self.zip.text auslösen kann , wenn ich diese Ansicht zum richtigen Zeitpunkt verwerfe und nicht self.zip einbinde.

Christopher Pickslay
quelle
4
Ich verstehe auch, dass weakEigenschaften nicht berücksichtigt werden müssen viewDidUnload. Aber warum enthält Apples Vorlage zum Erstellen von Verkaufsstellen eine [self setMySubview:nil]?
Yang Meyer
3
Gibt es Fälle in der Praxis, in denen die Verwendung von strong / beibehalten für Ihr IBOutlet Probleme verursachen kann? Oder ist es nur eine redundante Aufbewahrung, was einen schlechten Codierungsstil bedeutet, aber Ihren Code nicht beeinflusst?
Enzo Tran
1
Gibt es so etwas wie eine redundante Aufbewahrung? Wenn es eine zusätzliche Aufbewahrung gibt, wird diese nicht richtig gezählt und daher nicht freigegeben, sobald dies möglich ist, da die Aufbewahrungszahl eine zusätzliche Aufbewahrung enthält.
karlbecker_com
25

IBOutletsollte aus Leistungsgründen stark sein. Siehe Storyboard-Referenz, Starkes IBOutlet, Szenendock in iOS 9

Wie in diesem Absatz erläutert, können die Ausgänge zu Unteransichten der Ansicht des Ansichtscontrollers schwach sein, da diese Unteransichten bereits dem Objekt der obersten Ebene der NIB-Datei gehören. Wenn ein Outlet jedoch als schwacher Zeiger definiert und der Zeiger gesetzt ist, ruft ARC die Laufzeitfunktion auf:

id objc_storeWeak(id *object, id value);

Dadurch wird der Zeiger (Objekt) zu einer Tabelle hinzugefügt, wobei der Objektwert als Schlüssel verwendet wird. Diese Tabelle wird als schwache Tabelle bezeichnet. ARC verwendet diese Tabelle, um alle schwachen Zeiger Ihrer Anwendung zu speichern. Wenn der Objektwert freigegeben wird, durchläuft ARC die schwache Tabelle und setzt die schwache Referenz auf Null. Alternativ kann ARC anrufen:

void objc_destroyWeak(id * object)

Dann wird das Objekt nicht registriert und objc_destroyWeak ruft erneut auf:

objc_storeWeak(id *object, nil)

Diese mit einer schwachen Referenz verbundene Buchhaltung kann zwei- bis dreimal länger dauern als die Veröffentlichung einer starken Referenz. Eine schwache Referenz führt also zu einem Overhead für die Laufzeit, den Sie vermeiden können, indem Sie Outlets einfach als stark definieren.

Ab Xcode 7 schlägt es vor strong

Wenn Sie sich die WWDC 2015-Sitzung 407 zum Implementieren von UI-Designs in Interface Builder ansehen , wird dies vorgeschlagen (Transkript von http://asciiwwdc.com/2015/sessions/407 ).

Und die letzte Option, auf die ich hinweisen möchte, ist der Speichertyp, der entweder stark oder schwach sein kann.

Im Allgemeinen sollten Sie Ihren Ausgang stark machen, insbesondere wenn Sie einen Ausgang mit einer Unteransicht oder einer Einschränkung verbinden, die nicht immer von der Ansichtshierarchie beibehalten wird.

Das einzige Mal, wenn Sie eine Steckdose wirklich schwach machen müssen, ist, wenn Sie eine benutzerdefinierte Ansicht haben, die auf etwas verweist, das die Ansichtshierarchie sichert, und dies wird im Allgemeinen nicht empfohlen.

Also werde ich stark wählen und auf Verbinden klicken, um meine Steckdose zu generieren.

onmyway133
quelle
1
Tolle Antwort, die den eigentlichen Grund erklärt
warum
Das ist gut und alles andere als ich habe Lecks von Gestenerkennern gesehen, die im Storyboard implementiert sind.
Thibaut Noah
1
Ich kann diese Zeile nicht verstehen. "Das einzige Mal, wenn Sie eine Steckdose wirklich schwach machen müssen, ist, wenn Sie eine benutzerdefinierte Ansicht haben, die auf etwas verweist, das die Ansichtshierarchie sichert, und dies wird im Allgemeinen nicht empfohlen." Irgendwelche Beispiele?
user1872384
Ich habe die Deinit-Zeit berechnet, die schwach und stark benötigt, und es ist genau das gleiche.
Touti
Aber in Kürze ist dies eher der Fall. Schwache Referenzen sind schneller.
Thesummersign
20

In der iOS-Entwicklung unterscheidet sich das Laden von NIB ein wenig von der Mac-Entwicklung.

In der Mac-Entwicklung ist ein IBOutlet normalerweise eine schwache Referenz: Wenn Sie eine Unterklasse von NSViewController haben, bleibt nur die Ansicht der obersten Ebene erhalten, und wenn Sie den Controller freigeben, werden alle Unteransichten und Ausgänge automatisch freigegeben.

UiViewController verwendet die Schlüsselwertcodierung, um die Ausgänge unter Verwendung starker Referenzen festzulegen. Wenn Sie also die Freigabe Ihres UIViewControllers aufheben, wird die Zuordnung der Draufsicht automatisch aufgehoben. Sie müssen jedoch auch alle Steckdosen in der Freigabemethode freigeben.

In diesem Beitrag von der Big Nerd Ranch behandeln sie dieses Thema und erklären auch, warum die Verwendung einer starken Referenz in IBOutlet keine gute Wahl ist (auch wenn dies in diesem Fall von Apple empfohlen wird).

Giuseppe
quelle
16
Es erklärt es per 2009. Mit ARC hat sich dies erheblich geändert.
Dafydd Williams
1
:( der Big Nerd Ranch Link ist tot… aber ich muss ihn wirklich lesen.
Motti Shneor
@MottiShneor Keine Sorge, es ist keine große Sache, da der Link ungefähr vor ARC war und nicht mehr relevant ist.
Sergey Grischyov
18

Eines möchte ich hier hervorheben, und zwar trotz der Aussagen der Apple-Ingenieure in ihrem eigenen WWDC 2015-Video hier:

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

Apple ändert ständig seine Meinung zu diesem Thema, was uns sagt, dass es keine einzige richtige Antwort auf diese Frage gibt. Um zu zeigen, dass selbst Apple-Ingenieure in diesem Bereich gespalten sind, werfen Sie einen Blick auf den neuesten Beispielcode von Apple, und Sie werden sehen, dass einige Leute schwach verwenden, andere nicht.

In diesem Apple Pay-Beispiel wird eine schwache Funktion verwendet: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewConCer_on

Ebenso wie dieses Bild-in-Bild-Beispiel: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP4001616Pon_Player_Player

Ebenso wie das Lister-Beispiel: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Wie auch das Beispiel für den Kernspeicherort: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_s

Wie funktioniert die Steuerung Vorschauen Beispiel Ansicht: https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift-DontLinkElementID_5

Ebenso wie das HomeKit-Beispiel: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP40015048-HeCatalog__

Alle diese sind für iOS 9 vollständig aktualisiert und alle verwenden schwache Steckdosen. Daraus lernen wir, dass A. Das Problem ist nicht so einfach, wie manche Leute es sich vorstellen. B. Apple hat seine Meinung wiederholt geändert und C. Sie können alles verwenden, was Sie glücklich macht :)

Besonderer Dank geht an Paul Hudson (Autor von www.hackingwithsift.com), der mir die Klarstellung und Referenzen für diese Antwort gegeben hat.

Ich hoffe das klärt das Thema etwas besser!

Pass auf.

Syedfa
quelle
Ich habe dieses Problem seit einiger Zeit überprüft und keine konkreten Antworten gefunden. Da der obige Link darauf hindeutet, dass beide in Ordnung sind und im Allgemeinen mit dem übereinstimmen, was Xcode automatisch vorschlägt.
Subin272
9

Ab WWDC 2015 gibt es eine Sitzung zum Implementieren von UI-Designs in Interface Builder . Rund um die 32min Marke , sagt er , dass Sie immer Ihre machen wollen @IBOutlet stark .

Johannes
quelle
Interessant. Ich denke, dies hat sich geändert, als das Entladen der Ansicht entfernt wurde.
Hypercrypt
6

Seien Sie sich bewusst, IBOutletCollectionsollte sein @property (strong, nonatomic).

Landonandrey
quelle
3
Warum nicht so copywie es ist NSArray?
Hypercrypt
5

Es sieht so aus, als ob sich im Laufe der Jahre etwas geändert hat, und jetzt empfiehlt Apple, generell stark zu verwenden. Die Beweise für ihre WWDC-Sitzung befinden sich in Sitzung 407 - Implementieren von UI-Designs in Interface Builder und beginnen um 32:30 Uhr. Meine Notiz von dem, was er sagt, ist (fast, wenn nicht genau, zitiert er ihn):

  • Steckdosenverbindungen sollten im Allgemeinen stark sein, insbesondere wenn wir eine Unteransicht oder Einschränkung verbinden, die nicht immer von der Ansichtshierarchie beibehalten wird

  • Möglicherweise ist eine schwache Steckdosenverbindung erforderlich, wenn benutzerdefinierte Ansichten erstellt werden, die auf eine Sicherung in der Ansichtshierarchie verweisen. Dies wird im Allgemeinen nicht empfohlen

Auf anderen Stationen sollte es jetzt immer stark sein, solange einige unserer benutzerdefinierten Ansichten keinen Aufbewahrungszyklus mit einigen der Ansichten in der Ansichtshierarchie erstellen

BEARBEITEN:

Einige mögen die Frage stellen. Wird durch das Beibehalten einer starken Referenz kein Aufbewahrungszyklus erstellt, da der Root-Ansichts-Controller und die besitzende Ansicht den Verweis darauf beibehalten? Oder warum ist das geändert worden? Ich denke, die Antwort ist früher in diesem Vortrag, wenn sie beschreiben, wie die Schreibfedern aus der xib erstellt werden. Es gibt eine separate Feder, die für eine VC und für die Ansicht erstellt wurde. Ich denke, dies könnte der Grund sein, warum sie die Empfehlungen ändern. Trotzdem wäre es schön, eine tiefere Erklärung von Apple zu bekommen.

Julian Król
quelle
4

Ich denke, dass die wichtigsten Informationen sind: Elemente in xib werden automatisch in Unteransichten der Ansicht angezeigt. Unteransichten ist NSArray. NSArray besitzt seine Elemente. usw. haben starke Hinweise auf sie. In den meisten Fällen möchten Sie also keinen weiteren starken Zeiger (IBOutlet) erstellen.

Und mit ARC müssen Sie nichts tun viewDidUnload

kraag22
quelle