Jaka jest różnica między bluszczami i właściwościami w Objective-C


82

Jaka jest semantyczna różnica między tymi trzema sposobami użycia bluszczów i właściwości w Objective-C?

1.

@class MyOtherObject; 
@interface MyObject {
}
@property (nonatomic, retain) MyOtherObject *otherObj;

2.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
@property (nonatomic, retain) MyOtherObject *otherObj;

3.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}

Odpowiedzi:


57

Numer 1 różni się od pozostałych dwóch deklaracją do przodu klasy MyOtherObject, aby zminimalizować ilość kodu widzianego przez kompilator i konsolidator, a także potencjalnie uniknąć cyklicznych odwołań. Jeśli robisz to w ten sposób, pamiętaj o umieszczeniu #import w pliku .m.

Deklarując @właściwość (i dopasowując @synthesize w pliku .m), automatycznie generujesz metody akcesorów z semantyką pamięci obsługiwaną w określony sposób. Podstawową zasadą dla większości obiektów jest Zachowaj, ale NSStrings, na przykład, powinno używać Kopiuj. Natomiast Singletons i Delegates zwykle powinni używać Assign. Akcesory do ręcznego pisania są żmudne i podatne na błędy, więc oszczędza to wiele błędów związanych z pisaniem i głupimi błędami.

Ponadto zadeklarowanie zsyntetyzowanej właściwości umożliwia wywołanie metody akcesora przy użyciu notacji kropkowej w następujący sposób:

self.otherObj = someOtherNewObject; // set it  
MyOtherObject *thingee = self.otherObj; // get it 

Zamiast normalnego sposobu przekazywania wiadomości:

[self setOtherObject:someOtherNewObject]; // set it
MyOtherObject *thingee = [self otherObj]; // get it 

Za kulisami naprawdę wywołujesz metodę, która wygląda następująco:

- (void) setOtherObj:(MyOtherObject *)anOtherObject {

    if (otherObject == anOtherObject) {
        return;  
    }

    MyOtherObject *oldOtherObject = otherObject; // keep a reference to the old value for a second
    otherObject = [anOtherObject retain]; // put the new value in  
    [oldOtherObject release]; // let go of the old object
} // set it

…albo to

- (MyOtherObject *) otherObject {  
    return otherObject;
} // get it

Całkowity ból w tyłku, prawda. Teraz zrób to dla każdego ivara w klasie. Jeśli nie zrobisz tego dokładnie dobrze, pojawi się wyciek pamięci. Najlepiej po prostu pozwolić kompilatorowi wykonać pracę.

Widzę, że numer 1 nie ma ivar. Zakładając, że to nie jest literówka, jest w porządku, ponieważ dyrektywy @property / @synthesize zadeklarują również ivar za kulisami. Uważam, że to nowość w systemie Mac OS X - Snow Leopard i iOS4.

Numer 3 nie ma wygenerowanych akcesorów, więc musisz je napisać samodzielnie. Jeśli chcesz, aby metody akcesorów miały efekty uboczne, wykonaj standardowy taniec zarządzania pamięcią, jak pokazano powyżej, a następnie wykonaj dowolną pracę poboczną w ramach metody akcesora. Jeśli syntetyzować nieruchomości , jak również pisać własne , a następnie swoją wersja posiada priorytet.

Czy omówiłem wszystko?


tak, dziękuję bardzo! Jedna uwaga, którą chciałbym powiedzieć, to to, że jeśli usuniesz pragmę klasy forward w # 1 i zastąpisz ją #import "MyOtherObject", otrzymasz błąd czasu kompilacji, ale nie wiem dlaczego ....
— ennuikiller

czy jest jakaś przewaga stosowania podejścia nr 2 nad podejściem nr 1?
— Greg,

@Greg Metoda nr 1 zapobiegnie odwołaniu cyklicznemu. Zobacz stackoverflow.com/questions/7221174/…
— willc2

3
Dobra odpowiedź, poza tym trochę o notacji kropkowej. Nie musisz syntetyzować właściwości, aby użyć jej w notacji kropkowej. W rzeczywistości nie musisz w ogóle deklarować właściwości. Dopóki masz zadeklarowany setter i getter (np. setFoo:I foo), możesz używać notacji kropkowej.
— JeremyP,

Dla istotności, jeśli używasz ARC, synteza jest wykonywana automatycznie.
— Sean Larkin

17

W dawnych czasach miałeś bluszcze i jeśli chciałeś pozwolić innej klasie ustawić je lub przeczytać, to musiałeś zdefiniować metodę pobierającą (tj. -(NSString *)foo)I ustawiającą (tj -(void)setFoo:(NSString *)aFoo;.).

To, co daje ci właściwości, to seter i getter za darmo (prawie!) Wraz z ivar. Więc kiedy teraz definiujesz właściwość, możesz ustawić atomowość (na przykład czy chcesz zezwolić na wiele działań ustawień z wielu wątków), a także przypisać / zachować / skopiować semantykę (to znaczy, czy ustawiający powinien skopiować nową wartość lub po prostu zapisz bieżącą wartość - ważne, jeśli inna klasa próbuje ustawić twoją właściwość string za pomocą mutowalnego ciągu, który może zostać zmieniony później).

To właśnie @synthesizerobi. Wiele osób pozostawia tę samą nazwę ivar, ale możesz ją zmienić podczas pisania instrukcji synthesize (tj. @synthesize foo=_foo;Oznacza utworzenie ivar o nazwie _foodla właściwości foo, więc jeśli chcesz czytać lub pisać tę właściwość, a nie używasz self.foo, trzeba użyć _foo = ...- po prostu pomaga złapać bezpośrednie odniesienia do ivar, jeśli chcesz przejść tylko przez setter i getter).

Od Xcode 4.6 nie musisz używać @synthesizeinstrukcji - kompilator zrobi to automatycznie i domyślnie doda nazwę ivar do nazwy ivar_ .


1
Należy zauważyć, że atomowość właściwości nie gwarantuje bezpieczeństwa wątków .
— jscs

Więc jeśli mam ivar, który jest atomowy, masz na myśli, że kiedy ustawiający go ustawia lub pobiera go getter, włącza się inny wątek i próbuje to zrobić, że wszystko się psuje? Więc jaki jest sens atomistyki? Rozumiem, że atomic przynajmniej upewnia się, że jeśli ustawisz ivar, zostanie ustawiony, jego licznik retencji jest odpowiedni itp. W przeciwnym razie, dlaczego atomowy? [Nie to, że rozwiązuje wszystkie problemy, po prostu zapobiega wpadaniu w kłopoty]
— David H

2
Masz gwarancję otrzymania prawidłowego, całego obiektu - funkcja pobierająca nie zwróci obiektu, który jest w trakcie zwalniania - ale jeśli inne wątki używają metody ustawiającej, możesz uzyskać wartość przed lub po. Określenie, które ma być obsługiwane poza metodami pobierającymi i ustawiającymi. Innymi słowy, żaden wątek nie zostanie przerwany podczas operacji pobierającej lub ustawiającej, ale kolejność operacji nie jest (nie może być na tym poziomie AFAIK) zdefiniowana.
— jscs

Cóż, argumentowałbym, że twój pierwotny komentarz był źle umieszczony - atomowość jest honorowana, po prostu dostęp przez wątki może prowadzić do wielu problemów - w ten sposób każdy ivar, który kiedykolwiek zadeklarowałem, jest atomowy, a jeśli są w to zaangażowane wątki, to współbieżność jest rozpatrywany gdzie indziej.
— David H
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.