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;
}
ennuikiller
źródło

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?

willc2
źródło
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_ .

David H.
źródło
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