Eigenschaftsattribute in Objective-C. Anleitung für Anfänger

    Bild

    Es gibt viele verschiedene Stellen, an denen die Bedeutung der Attribute beschrieben wird, die bei der Deklaration von Eigenschaften verwendet werden. In der Regel beschreiben alle Quellen jedoch entweder nur die Verwendung eines der Attribute oder enthalten eine Reihe von Links zu anderen Dokumenten. Natürlich müssen Sie sich bemühen, alle Feinheiten zu lernen, für die Sie Berge von Literatur lesen müssen. Für den Anfang ist es jedoch ausreichend, die Grundlagen zu verstehen. Im Folgenden habe ich versucht, die Hauptattribute für die Deklaration von Eigenschaften, ihren Wert und die Hauptfälle, in denen es sich lohnt, den einen oder anderen Attributwert zu verwenden, so einfach wie möglich zu erklären.

    Kurze Einführung


    Es ist empfehlenswert, auf die Daten eines Objekts in objektorientierten Sprachen mithilfe der Setter- und Getter-Methoden zuzugreifen (sie sind auch ein Mutator und ein Accessor), wobei erstere den Eigenschaftswert festlegen und letztere den Eigenschaftswert zurückgeben, anstatt direkt auf Instanzvariablen zuzugreifen. Um die Notwendigkeit zu beseitigen, eine Reihe von Methoden zu deklarieren, wurden Eigenschaften erfunden, die den erforderlichen Schreibaufwand verringerten, aber nichts an der Essenz änderten: Beim Zugriff auf die Eigenschaften wird auch ein Setter oder Getter aufgerufen, je nachdem, welche Operation automatisch generiert werden kann. Im Wesentlichen bestimmen die beim Deklarieren von Eigenschaften angegebenen Attribute, wie Datenzugriffsmethoden generiert werden.

    Direkt Anweisung


    Zunächst unterteilen wir alle Attribute, die eine Eigenschaft hat, in Gruppen:

    1. Eingabehilfen (readonly / readwrite),
    2. Eigentumsattribute (beibehalten / stark / kopieren / zuweisen / unsicher / schwach),
    3. Atomizitätsattribut (atomar / nichtatomar).
    4. Attribut nullability (null_unspecified / null_resettable / nullable / nonnull) - wurde in xcode 6.3 angezeigt

    Die Attribute, mit denen Sie den Namen von Setter und Getter festlegen können, werden nicht berücksichtigt. Für diese gibt es keine Regeln als solche, mit Ausnahme derjenigen, die der von Ihnen verwendete Codestil bereitstellt. Explizit oder implizit, aber für jede Eigenschaft werden Attribute aller Typen angegeben.

    Verfügbarkeitsattribute

    • readwrite - Gibt an, dass die Eigenschaft zum Lesen und Schreiben verfügbar ist, dh, sowohl ein Setter als auch ein Getter werden generiert. Dieser Wert ist standardmäßig auf alle Eigenschaften festgelegt, sofern nicht anders angegeben.

    • readonly - Gibt an, dass die Eigenschaft schreibgeschützt ist. Dieser Wert sollte in Fällen verwendet werden, in denen das Ändern der Eigenschaft "außerhalb" während der Ausführung der Aufgabe durch das Objekt unerwünscht ist oder wenn der Eigenschaftswert nicht in einer Variablen gespeichert ist, sondern auf der Grundlage der Werte anderer Eigenschaften generiert wird. Beispielsweise hat das User-Objekt die Eigenschaften firstName und lastName, und der Einfachheit halber wird die schreibgeschützte Eigenschaft fullName festgelegt, deren Wert auf den Werten der ersten beiden Eigenschaften basiert.

    In dem Fall, dass es unerwünscht ist, die Eigenschaft "outside" zu ändern, wird sie normalerweise in der Klassenschnittstelle als readonly deklariert und dann in der Klassenerweiterung als readwrite neu definiert, so dass es innerhalb der Klasse auch möglich ist, den Wert nicht direkt in der Variablen, sondern über den Setter zu ändern .

    Eigentümerattribute

    Dies ist der umfangreichste Attributtyp, der hauptsächlich auf die Koexistenz von manueller und automatischer Speicherverwaltung zurückzuführen ist.
    Wenn ARC aktiviert ist, weisen Variablen wie Eigenschaften ein Eigentumsattribut auf. Nur in diesem Fall ist der Wertesatz kleiner als die Eigenschaften: __strong / __ weak / __ unsafe_unretained. Dies gilt nur für Datentypen, die ARC unterliegen (Features für Typen) Daten, die nicht dem ARC unterliegen, werden nicht berücksichtigt, um das, was als einfacher Spickzettel gedacht ist, nicht zu verkomplizieren. Daher geben wir bei der Beschreibung der Werte dieses Attributs für Eigenschaften auch an, welchen Wert das Eigentumsattribut der entsprechenden Instanzvariablen haben soll, wenn ARC aktiviert ist (wenn die Variable automatisch erstellt wird, wird sie sofort mit dem gewünschten Wert dieses Attributs erstellt. Wenn Sie die Variable selbst definieren, müssen Sie dies manuell festlegen korrekter Besitzattributwert).

    • Beibehalten (die entsprechende Variable muss mit dem Attribut __strong versehen sein) - Dieser Wert gibt an, dass im generierten Setter die Referenzanzahl für das zugewiesene Objekt erhöht und für das Objekt, auf das die Eigenschaft zuvor Bezug genommen hat, die Referenzanzahl verringert wird. Dieser Wert gilt, wenn ARC für alle Objective-C-Klassen in allen Fällen deaktiviert ist, in denen keine anderen Werte geeignet sind. Dieser Wert wurde seit den Tagen beibehalten, an denen ARC noch nicht verwendet wurde, und obwohl ARC seine Verwendung nicht verbietet, ist es besser, statt der automatischen Verbindungszählung die Option "strong" zu verwenden.

    //примерно вот такой сеттер будет сгенерирован для свойства 
    //с таким значением атрибута владения с отключенным ARC
    -(void)setFoo(Foo *)foo {
      if (_foo != foo) {
        Foo *oldValue = _foo;
        //увеличивается счетчик ссылок на новый объект и укзатель на него сохраняется в ivar
        _foo = [foo retain];
        //уменьшается счетчик ссылок на объект, на который раньше указывало свойство
        [oldValue release]; 
      }
    }
    

    • strong (die entsprechende Variable muss mit dem Attribut __strong angegeben werden) - Dieser Wert entspricht der Beibehaltung, gilt jedoch nur, wenn die automatische Verbindungszählung aktiviert ist. Bei Verwendung von ARC wird dieser Wert standardmäßig verwendet. Verwenden Sie stark in allen Fällen, die nicht für schwach und kopieren geeignet sind, und alles wird gut.

    • copy (die entsprechende Variable muss das Attribut __strong haben) - Mit diesem Wert des Eigentumsattributs im generierten Setter wird der entsprechenden Instanzvariablen der Wert zugewiesen, der von der an das zugewiesene Objekt gesendeten Nachricht copy zurückgegeben wird. Durch die Verwendung dieses Werts des Eigentumsattributs werden der Klasse des Objekts einige Einschränkungen auferlegt:

    1. Die Klasse muss das NSCopying-Protokoll unterstützen.
    2. Die Klasse sollte nicht veränderlich sein. Einige Klassen haben eine veränderbare Unterklasse, z. B. NSString-NSMutableString. Wenn Ihre Eigenschaft eine Instanz einer "veränderlichen" Klasse ist, führt die Verwendung von copy zu unerwünschten Konsequenzen, da die copy-Methode eine Instanz ihres "nicht veränderlichen" Verwandten zurückgibt. Wenn Sie beispielsweise copy für eine Instanz von NSMutableString aufrufen, wird eine Instanz von NSString zurückgegeben.

    Ein Beispiel:

    @property (copy, nonatomic) NSMutableString *foo;
    	...
    	//в сгенерированном сеттере будет примерно следующее
    - (void)setFoo:(NSMutableString)foo {
        _foo = [foo copy]; //метод copy класса NSMutableString вернет объект типа NSString, так что после присвоения значения этому свойству, оно будет указывать на неизменяемую строку, и вызов методов, изменяющих строку, у этого объекта приведет к крэшу
    }
    

    Die zweite Einschränkung impliziert den Hauptgrund für die Verwendung des Kopierwerts: Alle öffentlichen Eigenschaften, die Instanzen einer Klasse mit einer „veränderlichen“ Unterklasse sind, werden am besten mit diesem Besitzattributwert erstellt. Bei nicht veränderlichen Klassen wird die Kopiermethode beibehalten. Es wird kein Kopiervorgang ausgeführt, zusätzlicher Speicher und zusätzliche Zeit werden nicht verbraucht, die Eigenschaft wird jedoch vor der Angabe einer Instanz der veränderlichen Unterklasse geschützt. Es ist beispielsweise unmöglich, ein Objekt der NSMutableArray-Klasse in eine Eigenschaft vom Typ NSArray zu setzen, und daher ist es unmöglich, die Eigenschaft „außerhalb“ zu ändern, indem der Setter umgangen wird.

    Ein Beispiel:

    @interface Foo : NSObject
    …
    @property (copy, nonatomic) NSArray *bar;
    @property (strong, nonatomic) NSArray *barNotProtected;
    …
    @end
    …
    NSMutableArray *mutableArray = [@[@1, @2, @3] mutableCopy];
    Foo *foo = [Foo new];
    foo.bar = mutableArray; //в bar будет указатель на неизменяемую копию массива
    foo.barNotProtected = mutableArray; //а в barNotProtected будет записан указатель на изменяемый массив
    [mutableArray removeObjectAtIndex:0]; //теперь foo.barNotProtected указывает на измененный массив (@[@2, @3]), а массив, на который указывает foo.bar никак не изменился (@[@1, @2, @3]).
    

    • schwach (die entsprechende Variable muss mit dem Attribut __weak versehen sein) - dieser Wert ist ähnlich wie assign und unsafe_unretained. Der Unterschied besteht darin, dass spezielle Straßenmagie es Variablen mit diesem Eigentumsattributwert ermöglicht, ihren Wert auf Null zu ändern, wenn das Objekt, auf das die Variable zeigt, zerstört wird, was sich auf die Stabilität der Anwendung auswirkt (da nil, wie Sie wissen, auf Nachrichten reagiert) , was bedeutet, dass Sie beim Zugriff auf ein bereits gelöschtes Objekt kein EXC_BAD_ACCESS haben). Dieser Wert des Eigentumsattributs sollte verwendet werden, wenn ARC aktiviert ist, um Beibehaltungszyklen für Eigenschaften auszuschließen, in denen ein Zeiger auf einen Objektdelegaten und dergleichen gespeichert ist. Dies ist der einzige Wert des Eigentumsattributs, der nicht unterstützt wird, wenn ARC deaktiviert ist (sowie wenn ARC unter iOS vor Version 5.0 aktiviert ist).

    • unsafe_unretained (die entsprechende Variable muss das Attribut __unsafe_unretained enthalten) - eine Eigenschaft mit diesem Eigentümertyp speichert einfach die Adresse des ihr zugewiesenen Objekts. Zugriffsmethoden auf diese Eigenschaft wirken sich nicht auf die Anzahl der Objektreferenzen aus. Es kann gelöscht werden, und der Zugriff auf diese Eigenschaft führt zu einem Absturz (und ist daher nicht sicher). Dieser Wert wurde anstelle von schwach verwendet, als ARC angezeigt wurde, aber iOS 4.3 musste weiterhin unterstützt werden. Jetzt kann seine Verwendung nur durch die Geschwindigkeit der Arbeit gerechtfertigt werden (es gibt Anzeichen dafür, dass die Magie schwacher Eigenschaften viel Zeit in Anspruch nimmt, obwohl Sie dies bei der aktuellen Leistung natürlich nicht mit bloßem Auge bemerken), daher sollten Sie sie insbesondere zunächst nicht verwenden.

    • assign (die entsprechende Variable sollte das Attribut __unsafe_unretained haben, aber da nur ARC-Typen, die besser stark oder schwach sind, Eigentumsattribute haben, ist es unwahrscheinlich, dass dieser Wert benötigt wird) - einfach eine Adresse zuweisen. Ohne ARC der Standardwert des Eigentumsattributs. Es sollte auf Eigenschaften von Typen angewendet werden, die nicht ARC unterliegen (dazu gehören primitive Typen und sogenannte Nicht-Objekttypen (wie CFTypeRef). Ohne ARC wird es auch anstelle von schwach verwendet, um Beibehaltungszyklen für Eigenschaften auszuschließen, die einen Zeiger auf einen Objektdelegierten und dergleichen enthalten.

    Atomattribut

    • Atomic ist der Standardwert für dieses Attribut. Dies bedeutet, dass der Accessor und der Mutator so generiert werden, dass sie beim gleichzeitigen Zugriff von verschiedenen Threads nicht zur gleichen Zeit ausgeführt werden (das heißt, dass immer nur ein Thread seine Aufgabe erfüllt - fragen oder einen Wert abrufen und erst danach ein anderer Thread in der Lage sein, das gleiche zu tun). Aufgrund von Implementierungsbesonderheiten können Eigenschaften mit einem solchen Atomicity-Attributwert nicht nur eine der Zugriffsmethoden außer Kraft setzen (wenn Sie sie neu definieren, definieren Sie beide neu und verwechseln sie mit dem Schutz vor gleichzeitiger Ausführung in verschiedenen Threads). Verwechseln Sie die Atomizität einer Eigenschaft nicht mit der Fadensicherheit eines Objekts. Wenn Sie beispielsweise in einem Thread den Vor- und Nachnamenwert aus den Eigenschaften des Objekts abrufen und in dem anderen Thread dieselben Werte ändern, kann es durchaus so ausfallen: dass der Wert des Namens, den Sie erhalten, alt ist und der Nachname bereits geändert wurde. Dementsprechend sollte es für die Eigenschaften von Objekten verwendet werden, auf die von vielen Threads gleichzeitig zugegriffen werden kann, und es muss verhindert werden, dass ungültige Werte empfangen werden. Falls erforderlich, stellen Sie jedoch die vollständige Threadsicherheit sicher, dies allein reicht möglicherweise nicht aus. Unter anderem ist daran zu erinnern, dass Zugriffsmethoden mit solchen Eigenschaften langsamer arbeiten als nichtatomare, was natürlich eine Kleinigkeit auf der Skala des Universums ist, aber trotzdem spart ein Penny einen Rubel. Wenn dies nicht erforderlich ist, ist es besser, nichtatomare Methoden zu verwenden. Dies allein reicht jedoch möglicherweise nicht aus, um die vollständige Gewindesicherheit zu gewährleisten. Unter anderem ist daran zu erinnern, dass Zugriffsmethoden mit solchen Eigenschaften langsamer arbeiten als nichtatomare, was natürlich eine Kleinigkeit auf der Skala des Universums ist, aber trotzdem spart ein Penny einen Rubel. Wenn dies nicht erforderlich ist, ist es besser, nichtatomare Methoden zu verwenden. Dies allein reicht jedoch möglicherweise nicht aus, um die vollständige Gewindesicherheit zu gewährleisten. Unter anderem ist daran zu erinnern, dass Zugriffsmethoden mit solchen Eigenschaften langsamer arbeiten als nichtatomare, was natürlich eine Kleinigkeit auf der Skala des Universums ist, aber trotzdem spart ein Penny einen Rubel. Wenn dies nicht erforderlich ist, ist es besser, nichtatomare Methoden zu verwenden.

    • Nicht atomar - das Gegenteil von atomar - Bei Eigenschaften mit diesem Attributwert atomicity sind Zugriffsmethoden nicht mit dem Schutz vor gleichzeitiger Ausführung in verschiedenen Threads belastet, daher sind sie schneller. Dieser Wert ist für die meisten Eigenschaften geeignet, da die meisten Objekte dennoch nur in einem Thread verwendet werden und es keinen Sinn macht, sie mit zusätzlichen Features zu „gewichten“. Im Allgemeinen sollten Sie für alle Eigenschaften, für die Sie nicht erklären können, warum sie atomar sein sollten, nichtatomar verwenden, und Sie werden schnell und einfach und intelligent sein und nur eine Art Urlaub.

    Nullability-Attribut

    Dieses Attribut hat keine Auswirkung auf die generierten Zugriffsmethoden. Es soll angeben, ob eine bestimmte Eigenschaft null oder null sein kann. Xcode verwendet diese Informationen bei der Interaktion mit Swift-Code in Ihrer Klasse. Darüber hinaus wird dieser Wert verwendet, um Warnungen anzuzeigen, falls Ihr Code dem deklarierten Verhalten widerspricht. Zum Beispiel, wenn Sie versuchen, nil auf eine Eigenschaft zu setzen, die als nicht ungültig deklariert ist. Und am wichtigsten ist, dass diese Informationen für jemanden nützlich sind, der Ihren Code liest. Für die Verwendung dieses Attributs gelten einige Einschränkungen:
    1. Es kann nicht für primitive Typen verwendet werden (sie benötigen es nicht, da sie auf keinen Fall Null- und Nullwerte akzeptieren).
    2. es kann nicht für mehrstufige Zeiger verwendet werden (z. B. id * oder NSError **)

    • null_unspecified - wird standardmäßig verwendet und sagt nichts darüber aus, ob die Eigenschaft nil / NULL sein kann oder nicht. Vor dem Erscheinen dieses Attributs haben wir genau so alle Eigenschaften wahrgenommen. Dies ist in Bezug auf den Inhalt der Header schlecht, daher wird die Verwendung dieses Werts nicht empfohlen.
    • null_resettable - Dieser Wert gibt an, dass der Getter einer solchen Eigenschaft niemals nil / NULL zurückgibt, da der Eigenschaft beim Festlegen dieses Werts tatsächlich ein Standardwert zugewiesen wird. Und da die generierten Zugriffsmethoden nicht vom Wert dieses Attributs abhängen, müssen Sie entweder den Setter neu definieren, sodass er beim Empfang von nil / NULL den Standardwert in ivar beibehält, oder den Getter neu definieren, sodass er den Standardwert zurückgibt, wenn wenn der entsprechende ivar == nil / NULL ist. Wenn Ihre Eigenschaft einen Standardwert hat, deklarieren Sie ihn dementsprechend als null_resettable.
    • nonnull - Dieser Wert gibt an, dass die mit diesem Attribut markierte Eigenschaft nicht nil / NULL ist. Tatsächlich können Sie immer noch nil erhalten, wenn Sie beispielsweise versuchen, den Wert dieser Eigenschaft von nil zu erhalten, und einfach, weil Xcode dies nicht sehr genau befolgt. Dies ist jedoch bereits Ihr Fehler, und Xcode weist in seinen Warnungen nachdrücklich darauf hin. Es lohnt sich zu verwenden, wenn Sie sicher sind, dass der Wert dieser Eigenschaft niemals nil / NULL ist und Sie möchten, dass die IDE Sie dabei unterstützt, den Überblick zu behalten.
    • nullable - dieser Wert gibt an, dass die Eigenschaft nil / NULL sein kann. Genau wie null_unspecified ist es zu nichts verpflichtet, aber aufgrund seiner größeren Gewissheit ist es korrekter, nullable unter diesen beiden zu verwenden. Wenn Ihnen weder null_resettable noch nonnull zusagt, verwenden Sie nullable.

    Der Einfachheit halber können Sie den Standardwert für einen bestimmten Codeblock von null_unspecified in nonnull ändern. Stellen Sie dazu NS_ASSUME_NONNULL_BEGIN vor einen solchen Block und NS_ASSUME_NONNULL_END danach.
    //например вот такое объявление
    @property (copy, nonatomic, nonnull) NSString *foo;
    @property (copy, nonatomic, nonnull) NSString *str;
    @property (strong, nonatomic, nullable) NSNumber *bar;
    @property (copy, nonatomic, null_resettable) NSString *baz;
    //аналогично следующему блоку
    NS_ASSUME_NONNULL_BEGIN
    @property (copy, nonatomic) NSString *foo;
    @property (copy, nonatomic) NSString *str;
    @property (strong, nonatomic, nullable) NSNumber *bar;
    @property (copy, nonatomic, null_resettable) NSString *baz;
    NS_ASSUME_NONNULL_END
    


    Darauf beeile ich mich, die Geschichte zu beenden. Wer hat etwas hinzuzufügen oder etwas zu streiten - willkommen in Kommentaren.

    Update: Dank storoj wurde das Setter-Beispiel im Item über Retain behoben und das Item über Atomic wurde leicht verfeinert. Danke für die Kommentare.
    Upd2: Dank der Überzeugungskraft von fsmorygo wurde ein Abschnitt zum Attribut nullability hinzugefügt

    Jetzt auch beliebt: