574 Stimmen

Sollten IBOutlets unter ARC stark oder schwach sein?

Ich entwickle ausschließlich für iOS 5 mit ARC. Sollten IBOutlets zu UIViews (und Unterklassen) strong oder weak sein?

Das Folgende:

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

Würde das hier loswerden:

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

Gibt es dabei irgendwelche Probleme? Die Vorlagen verwenden strong, genauso wie die automatisch generierten Eigenschaften, die erstellt werden, wenn man direkt aus dem 'Interface Builder'-Editor mit dem Header verbindet, aber warum? Der UIViewController hat bereits eine strong-Referenz zu seinem view, der seine Subviews behält.

11 Stimmen

Als Hinweis, IBOutletCollection() darf nicht weak sein, ansonsten wird es als nil zurückgegeben.

0 Stimmen

Xcode 8.2.1 verwendet weak, wenn IBOutlets über den Interface Builder erstellt werden. Viele Antworten hier auf SO raten jedoch, strong zu verwenden.

1 Stimmen

@neoneye Ich habe es gerade mit xcode 8.3.2 ausprobiert, indem ich vom Storyboard zur Swift-Datei gezogen habe, und es wird standardmäßig auf strong festgelegt.

455voto

Alexsander Akers Punkte 15879

WARNUNG, VERALTETE ANTWORT: Diese Antwort ist nicht auf dem neuesten Stand gemäß WWDC 2015, für die richtige Antwort siehe die akzeptierte Antwort (Daniel Hall) oben. Diese Antwort bleibt als Aufzeichnung bestehen.


Zusammengefasst aus der Entwicklerbibliothek:

Aus praktischer Sicht sollten in iOS und OS X Outlets als deklarierte Eigenschaften definiert werden. Outlets sollten im Allgemeinen weak sein, außer für diejenigen vom File’s Owner zu Top-Level-Objekten in einer Nib-Datei (oder auf iOS, einer Storyboard-Szene), die sollten stark sein. Outlets, die Sie erstellen, sind daher in der Regel standardmäßig weak, denn:

  • Outlets, die Sie beispielsweise zu Subviews der Ansicht eines View-Controllers oder eines Fenstercontrollers erstellen, sind beliebige Verweise zwischen Objekten, die kein Eigentum implizieren.

  • Die starken Outlets werden häufig von Framework-Klassen spezifiziert (zum Beispiel der ViewController's view Outlet oder der NSWindowController's window Outlet).

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

10 Stimmen

Wie haben Sie den Link zur "Entwicklerbibliothek" dazu gebracht, an eine bestimmte Stelle der Apple-Dokumentenseite zu springen? Immer, wenn ich zu den Apple-Dokumenten verlinke, springt der Link immer an den Anfang der Seite (auch wenn der interessante Inhalt sich auf halber Seite befindet). Danke.

69 Stimmen

Ich habe den Link aus dem Navigationsbereich auf der linken Seite kopiert. :D

28 Stimmen

Was bedeutet "außer von Dateibesitzer zu übergeordneten Objekten in einer NIB-Datei (oder auf iOS, einer Storyboard-Szene)"?

287voto

Daniel Hall Punkte 13229

Die aktuelle empfohlene bewährte Praxis von Apple besteht darin, dass IBOutlets stark sein sollen, es sei denn, schwach wird speziell benötigt, um einen Retain-Zyklus zu vermeiden. Wie Johannes bereits erwähnt hat, wurde dies in der "Implementierung von UI-Designs in Interface Builder" Sitzung der WWDC 2015 kommentiert, wo ein Apple-Ingenieur sagte:

Und die letzte Option, die ich erwähnen möchte, ist der Speichertyp, der entweder stark oder schwach sein kann. Im Allgemeinen sollten Sie Ihren Outlet stark machen, insbesondere wenn Sie ein Outlet mit einer Unteransicht oder mit einer Einschränkung verbinden, die nicht immer von der Ansichtshierarchie gehalten wird. Die einzige Zeit, in der Sie ein Outlet wirklich schwach machen müssen, ist, wenn Sie eine benutzerdefinierte Ansicht haben, die auf etwas in der Ansichtshierarchie zurückverweist, und das ist im Allgemeinen nicht empfohlen.

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

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

38 Stimmen

Ist das wirklich wahr oder ist die Antwort mit 300+ Upvotes die Richtige? Mir ist aufgefallen, dass InterfaceBuilder standardmäßig weak verwendet, wenn Sie vom Storyboard auf die .h ziehen.

4 Stimmen

Derjenige mit 400+ Stimmen ist korrekt, aber veraltet. Seit iOS 6 wird viewDidUnload nicht aufgerufen, daher gibt es keine Vorteile, schwache Outlets zu haben.

8 Stimmen

@kjam es gibt Vorteile. Zuerst und vor allem solltest du keine starke Referenz zu etwas halten, das du nicht erstellt hast. Zweitens ist der Leistungsgewinn vernachlässigbar. Verstoße nicht gegen bewährte Praktiken in der Programmierung, nur weil jemand, selbst ein gut platzierter jemand, gesagt hat, dass dies 10 Mikrosekunden schneller ist. Schreibe klaren und verständlichen Code, versuche nicht, einen optimierenden Compiler zu spielen. Schreibe nur für die Leistung, wenn dies in einem spezifischen Fall als Problem festgestellt wurde.

52voto

Tammo Freese Punkte 10179

Obwohl die Dokumentation empfiehlt, weak für Eigenschaften von Subviews zu verwenden, scheint es seit iOS 6 in Ordnung zu sein, stattdessen strong (den Standardownership-qualifier) zu verwenden. Dies wird durch die Änderung in UIViewController verursacht, dass Views nicht mehr entladen werden.

  • Vor iOS 6, wenn starke Verknüpfungen zu Subviews der Hauptansicht des View Controllers gehalten wurden, würden diese die Subviews festhalten, solange der View Controller vorhanden ist, wenn die Hauptansicht des View Controllers entladen wurde.
  • Seit iOS 6 werden Views nicht mehr entladen, sondern einmal geladen und bleiben dann so lange vorhanden, wie ihr Controller da ist. Deshalb spielen starke Eigenschaften keine Rolle. Sie erzeugen auch keine starken Referenzzyklen, da sie entlang des starken Referenzgraphen zeigen.

In diesem Sinne bin ich hin- und hergerissen zwischen der Verwendung von

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

und

@property (nonatomic) IBOutlet UIButton *button;

in iOS 6 und danach:

  • Die Verwendung von weak zeigt deutlich an, dass der Controller keine Eigentümerschaft über den Button haben will.

  • Aber das Weglassen von weak schadet in iOS 6 ohne View-Entladung nicht und ist kürzer. Einige könnten darauf hinweisen, dass es auch schneller ist, aber ich bin noch keinen App begegnet, die aufgrund von weak IBOutlets zu langsam ist.

  • Das Nichtverwenden von weak könnte als Fehler angesehen werden.

Fazit: Seit iOS 6 können wir das nicht mehr falsch machen, solange wir keine View-Entladung verwenden. Zeit zum Feiern. ;)

0 Stimmen

Das stimmt, aber du könntest den View immer noch manuell entladen wollen. In diesem Fall müsstest du alle deine Outlets manuell auf nil setzen.

0 Stimmen

PS: weak ist in ARM64 ziemlich viel günstiger :D

0 Stimmen

Das stimmt, wenn Sie die Ansichtsentladung implementieren, sind weak-Eigenschaften oder __weak-Instanzvariablen der richtige Weg. Ich wollte nur darauf hinweisen, dass hier weniger Fehlerpotenzial besteht. Was weak betrifft, dass es billiger auf arm64 ist, habe ich noch nicht einmal ein echtes Leistungsproblem mit weak IBOutlets auf armv7 gesehen. :)

34voto

Christopher Pickslay Punkte 17247

Ich sehe kein Problem damit. Vor ARC habe ich immer meine IBOutlets mit assign erstellt, da sie bereits von ihren Übergeordneten Objekten gehalten werden. Wenn du sie als weak deklarierst, musst du sie nicht in viewDidUnload auf nil setzen, wie du bereits angemerkt hast.

Eine Einschränkung: Du kannst in einem ARC-Projekt iOS 4.x unterstützen, aber in diesem Fall kannst du kein weak verwenden, du müsstest sie also als assign deklarieren, in welchem Fall du den Verweis in viewDidUnload immer noch auf nil setzen müsstest, um einen hängenden Zeiger zu vermeiden. Hier ist ein Beispiel für einen hängenden Zeiger-Bug, den ich erlebt habe:

Ein UIViewController hat ein UITextField für die Postleitzahl. Es verwendet CLLocationManager, um den Standort des Benutzers zu umgekehren und die Postleitzahl festzulegen. Hier ist der Delegate-Rückruf:

-(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 habe festgestellt, dass wenn ich dieses Ansicht zur richtigen Zeit schließe und self.zip in viewDidUnload nicht auf nil setze, der Delegate-Rückruf eine Bad Access-Ausnahme für self.zip.text auslösen könnte.

4 Stimmen

Es ist auch mein Verständnis, dass schwache Eigenschaften nicht in viewDidUnload genullt werden müssen. Aber warum enthält das Apple-Template zur Erstellung von Outlets ein [self setMySubview:nil]?

3 Stimmen

Gibt es reale Fälle, in denen es Probleme verursachen könnte, wenn Sie strong/retained für Ihr IBOutlet verwenden? Oder handelt es sich nur um ein redundanten retain, was bedeutet, dass es schlechter Programmierstil ist, aber Ihren Code nicht beeinträchtigt?

1 Stimmen

Gibt es so etwas wie einen überflüssigen Retain? Wenn es einen zusätzlichen Retain gibt, wird dies dazu führen, dass er nicht ordnungsgemäß gezählt wird und daher nicht so schnell freigegeben wird, wie es könnte, da es eine zusätzliche Aufbewahrung in seinem Aufbewahrungszähler gibt.

27voto

onmyway133 Punkte 42296

IBOutlet sollte aus Leistungsgründen stark sein. Siehe Storyboard-Referenz, Strong IBOutlet, Szene-Dock in iOS 9

Wie in diesem Absatz erläutert, können die Outlets für Unteransichten der Ansicht des Ansichtscontrollers schwach sein, da diese Unteransichten bereits vom Top-Level-Objekt der Nib-Datei besessen werden. Wenn jedoch ein Outlet als schwacher Zeiger definiert ist und der Zeiger gesetzt wird, ruft ARC die Laufzeitfunktion auf:

id objc_storeWeak(id *object, id value);

Dies fügt den Zeiger (Objekt) zu einer Tabelle hinzu, 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 Deallokiert wird, wird ARC über die schwache Tabelle iterieren und die schwache Referenz auf nil setzen. Alternativ kann ARC aufrufen:

void objc_destroyWeak(id * object)

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

objc_storeWeak(id *object, nil)

Diese Buchführung, die mit einer schwachen Referenz verbunden ist, kann 2-3 mal länger dauern als die Freigabe einer starken Referenz. Daher führt eine schwache Referenz zu einem Overhead für die Laufzeit, den Sie einfach vermeiden können, indem Sie Outlets als stark definieren.

Ab Xcode 7 wird strong vorgeschlagen

Wenn Sie sich die WWDC 2015 Session 407 Implementieren von Benutzeroberflächendesigns in Interface Builder ansehen, wird (Transkript von http://asciiwwdc.com/2015/sessions/407) vorgeschlagen

Und die letzte Option, die ich erwähnen möchte, ist der Speichertyp, der entweder stark oder schwach sein kann.

Im Allgemeinen sollten Sie Ihr Outlet stark machen, insbesondere wenn Sie ein Outlet mit einer Unteransicht oder einer Einschränkung verbinden, die nicht immer von der Ansichtshierarchie aufrechterhalten wird.

Die einzige Situation, in der Sie wirklich ein Outlet schwach machen müssen, ist, wenn Sie eine benutzerdefinierte Ansicht haben, die auf etwas in der Ansichtshierarchie zurückverweist, und im Allgemeinen wird dies nicht empfohlen.

Also ich werde stark wählen und ich werde auf Verbinden klicken, was mein Outlet generieren wird.

1 Stimmen

Großartige Antwort, die den tatsächlichen Grund erklärt.

0 Stimmen

Das ist gut und alles, aber ich habe Lecks gesehen, die von Gestenerkennern, die im Storyboard implementiert sind, ausgehen.

1 Stimmen

Ich kann diese Zeile nicht verstehen. "Die einzige Zeit, die du wirklich ein Outlet schwach machen musst, ist, wenn du eine benutzerdefinierte Ansicht hast, die auf etwas in der Ansichtshierarchie zurückverweist, und im Allgemeinen wird das nicht empfohlen." Irgendwelche Beispiele?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X