Kiedy powinienem jawnie używać @synthesize?

81

O ile wiem, od XCode 4.4 @synthesizeautomatycznie wygeneruje metody dostępu do właściwości. Ale właśnie teraz przeczytałem przykładowy kod na temat NSUndoManager, aw kodzie zauważyłem, że @synthesizejest dodany jawnie. Lubić:

@interface RootViewController  ()

@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSUndoManager *undoManager;

@end

@implementation RootViewController
//Must explicitly synthesize this
@synthesize undoManager;

Jestem teraz zdziwiony ... Kiedy powinienem @synthesizewyraźnie dodać do mojego kodu?

罗泽轩
źródło
1
Przykładowy kod może być stary. Zasadniczo używaj go, chyba że stanie się to problemem (na przykład właściwości w delegatach nie będą automatycznie syntetyzowane)
borrrden
1
Spróbuj skomentować plik @sythesize. Jeśli kod nadal działa, nie jest to konieczne.
ThomasW

Odpowiedzi:

171

Jest wiele odpowiedzi, ale także duże zamieszanie. Postaram się trochę uporządkować (albo zwiększyć bałagan, zobaczymy ...)

  1. Przestańmy mówić o Xcode. Xcode to IDE . clang to kompilator . Ta funkcja, o której rozmawiamy, nazywa się autosyntezą właściwości i jest rozszerzeniem języka Objective-C obsługiwanym przez clang , który jest domyślnym kompilatorem używanym przez Xcode.
    Dla jasności, jeśli przełączysz się na gcc w Xcode, nie odniesiesz korzyści z tej funkcji (niezależnie od wersji Xcode). W ten sam sposób, jeśli używasz edytora tekstu i kompilujesz za pomocą clang z wiersza poleceń, Wola.

  2. Dzięki autosyntezie nie musisz jawnie syntetyzować właściwości, ponieważ zostanie ona automatycznie zsyntetyzowana przez kompilator jako

    @synthesize propertyName = _propertyName
    

    Istnieje jednak kilka wyjątków:

    • readwrite z niestandardowym narzędziem pobierającym i ustawiającym

      przy świadczeniu zarówno implementację getter i setter niestandardową właściwość nie zostanie automatycznie syntetyzowane

    • readonly z niestandardowym narzędziem pobierającym

      podczas dostarczania niestandardowej implementacji pobierającej dla właściwości tylko do odczytu, nie zostanie to automatycznie zsyntetyzowane

    • @dynamiczny

      podczas używania @dynamic propertyNamewłaściwość nie zostanie automatycznie zsyntetyzowana (dość oczywiste, ponieważ @dynamici @synthesizewzajemnie się wykluczają)

    • właściwości zadeklarowane w @protocol

      w przypadku zgodności z protokołem żadna właściwość zdefiniowana przez protokół nie będzie automatycznie syntetyzowana

    • właściwości zadeklarowane w kategorii

      jest to przypadek, w którym @synthesizedyrektywa nie jest automatycznie wstawiana przez kompilator, ale tych właściwości nie można również zsyntetyzować ręcznie. Chociaż kategorie mogą deklarować właściwości, nie można ich w ogóle syntetyzować, ponieważ kategorie nie mogą tworzyć wartości ivarów. Ze względu na kompletność dodam, że nadal można sfałszować syntezę właściwości przy użyciu środowiska uruchomieniowego Objective-C .

    • nadpisane właściwości (nowe od clang-600.0.51, dostarczane z Xcode 6, dzięki Marc Schlüpmann)

      kiedy nadpisujesz właściwość nadklasy, musisz jawnie ją zsyntetyzować

Warto zauważyć, że syntetyzowanie właściwości powoduje automatyczną syntezę kopii zapasowej ivar, więc jeśli brakuje syntezy właściwości, brak będzie również wartości ivar, chyba że zostanie to wyraźnie zadeklarowane.

Z wyjątkiem trzech ostatnich przypadków, ogólna filozofia polega na tym, że za każdym razem, gdy ręcznie określisz wszystkie informacje o właściwości (poprzez zaimplementowanie wszystkich metod @dynamicdostępu lub użycie ), kompilator założy, że chcesz mieć pełną kontrolę nad właściwością i wyłączy to.

Poza przypadkami wymienionymi powyżej, jedynym innym zastosowaniem wyraźnego określenia @synthesizebyłoby podanie innej nazwy ivar. Jednak konwencje są ważne, więc radzę zawsze używać domyślnego nazewnictwa.

Gabriele Petronella
źródło
Jeśli pierwotny pytający nadal tu jest, myślę, że należy zaakceptować tę odpowiedź. Jest najbardziej kompletny.
Steven Fisher
3
O ile dobrze pamiętam, właściwości określone w kategorii również nie są syntetyzowane automatycznie. (Przyczyną jest to, że nie można dodać zmiennej instancji w kategorii.)
Martin R
@MartinR, dobra uwaga. Nie są one syntetyzowane automatycznie, ale nie można ich również syntetyzować ręcznie, ponieważ @synthesizew kategorii jest to zabronione (nie ma żadnych wartości w kategoriach, jak już zauważyłeś). Dodam notatkę.
Gabriele Petronella,
Ale ta odpowiedź nie dotyczy konkretnego pytania: KIEDY należy używać @synthesize? Zawsze, nigdy, tylko wtedy, gdy spełnione są określone warunki?
Jeff
21

Jeśli nie użyjesz jawnie, @synthesizekompilator zrozumie twoją własność w taki sam sposób, jak napisałeś

@synthesize undoManager=_undoManager;

wtedy będziesz mógł pisać w swoim kodzie takie rzeczy jak:

[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

To jest powszechna konwencja.

jeśli piszesz

@synthesize undoManager;

będziesz miał :

[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Osobiście przestaję używać @synthesize, ponieważ nie jest to już obowiązkowe. Dla mnie jedynym powodem do użycia @synthesizejest połączenie pliku iVarz @property. Jeśli chcesz wygenerować określony getter i setter dla niego. Ale w danym fragmencie kodu nie ma iVar, myślę, że @synthesizejest to bezużyteczne. Ale teraz myślę, że nowe pytanie brzmi: „Kiedy używać iVar?” I nie mam innej odpowiedzi niż „nigdy” na to pytanie!

KIDdAe
źródło
2
Zgoda, przestałem używać, @synthesizeponieważ nie ma już powodu, aby to robić. Również wiodące podkreślenie służy jako miła wizualna flaga, informująca, że ​​pracujesz nad siecią bezpieczeństwa zarządzania pamięcią i wątkami, którą zapewniają właściwości (które powinieneś pominąć initi deallocmetody).
BergQuester
1
Pytanie brzmiało, kiedy trzeba wyraźnie dokonać syntezy. W ogóle na to nie odpowiedziałeś.
Fogmeister
1
Odkryłem dzisiaj, że to nie do końca prawda. Bez @synthesize odpowiadasz za utworzenie zmiennej wystąpienia zapasowego. Dzięki niemu kompilator zrobi to za Ciebie.
Steven Fisher
1
I nie głosowałem nikogo za tym odkryciem. :)
Steven Fisher
1
-1 całkowicie brakuje Ci przypadków, w których potrzebujesz jawnej syntezy. Jest to również funkcja kompilatora, a nie Xcode.
Gabriele Petronella
14

Kiedy należy @synthesizewyraźnie dodać do mojego kodu?

Ogólnie rzecz biorąc, jeśli jest to wymagane: prawdopodobnie nigdy nie trafisz na przypadek, w którym jest to potrzebne.

Jest jednak jeden przypadek, w którym może Ci się to przydać.

Załóżmy, że piszesz zarówno niestandardową metodę pobierającą, jak i ustawiającą, ale chcesz, aby zmienna instancji ją wspierała. (W przypadku właściwości atomowej jest to tak proste, jak potrzeba niestandardowego ustawiacza: kompilator zapisze metodę pobierającą, jeśli określisz metodę ustawiającą dla właściwości jednoatomowej, ale nie dla właściwości atomowej).

Rozważ to:

@interface MyObject:NSObject
@property (copy) NSString *title;
@end

@implementation MyObject

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

To nie zadziała, ponieważ _titlenie istnieje. Określono zarówno metodę pobierającą, jak i ustawiającą, więc Xcode (poprawnie) nie tworzy dla niej zmiennej instancji zapasowej.

wprowadź opis obrazu tutaj

Masz dwie możliwości zaistnienia. Możesz zmienić na @implementationto:

@implementation MyObject {
    NSString *_title;
}

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Lub zmień to na:

@implementation MyObject

@synthesize title = _title;

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Innymi słowy, chociaż syntetyzacja nie jest nigdy konieczna do celów praktycznych *, może być używana do definiowania zmiennych instancji obsługujących właściwości, gdy udostępniasz metodę pobierającą / ustawiającą. Możesz zdecydować, którego formularza tutaj chcesz użyć.

W przeszłości preferowałem określanie zmiennej instancji w elemencie @implementation {}, ale teraz uważam, że @synthesizetrasa jest lepszym wyborem, ponieważ usuwa nadmiarowy typ i wyraźnie wiąże zmienną zapasową z właściwością:

  1. Zmień typ właściwości, a typ zmiennej instancji ulegnie zmianie.
  2. Zmień jego kwalifikator magazynu (na przykład uczyń go słabym zamiast silnym lub silnym zamiast słabego) i zmieni się kwalifikator magazynu.
  3. Usuń lub zmień nazwę właściwości, a @synthesizespowoduje wygenerowanie błędu kompilatora. Nie skończysz z bezpańskimi zmiennymi instancji.

* - Znam jeden przypadek, w którym było to konieczne, związany z podziałem funkcji na kategorie w wielu plikach. I nie zdziwiłbym się, gdyby Apple to naprawił, a nawet już to zrobił.

Steven Fisher
źródło
Jesteś pewny? Czy nawet próbowałeś? Napisałem wiele własnych metod ustawiających i pobierających i NIGDY nie syntetyzowałem swoich właściwości. (I użyłem twojego pierwszego przykładu, o którym powiedziałeś, że nie działa)
Marc,
Tak, jestem pewny. Kod został skopiowany z nowego projektu. Nie będzie działać na najnowszych wersjach Xcode, chyba że określisz nieatomową we właściwości.
Steven Fisher
1
tak to prawda .. ale 99% czasu twoje właściwości są nieatomowe. Więc tylko w rzadkich przypadkach, gdy twoja właściwość jest atomowa i chcesz mieć niestandardowy getter / setter, który faktycznie musisz zsyntetyzować.
Marc
Jeśli zapewnisz niestandardową implementację zarówno dla metody ustawiającej, jak i pobierającej, kompilator założy, że przejmujesz kontrolę nad właściwością i nie zsyntetyzuje jej za Ciebie.
Gabriele Petronella
2
Zgadzam się z Tobą, chociaż rozumiem, dlaczego Apple początkowo uważało to za dobry pomysł. Prawdziwym problemem jest to, że atomicdopiero później dodano jako specyfikator właściwości. Teraz, gdy już tam jest, flaga ostrzegawcza może Cię CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESzainteresować.
Steven Fisher
7

OK, kiedy tworzysz nieruchomość ...

@property NSString *name;

Xcode automatycznie zsyntetyzuje iVar, tak jakbyś napisał ...

@synthesize name = _name;

Oznacza to, że możesz uzyskać dostęp do nieruchomości za pomocą ...

self.name;
// or
_name;

Każda z nich będzie działać, ale w self.namerzeczywistości używa tylko metod akcesorów.

Jest tylko jeden przypadek, w którym automatyczna synteza nie działa: Jeśli nadpisujesz, ale ustawiasz ORAZ metodę pobierającą, będziesz musiał zsyntetyzować iVar.

Nic ci nie jest, jeśli po prostu zastąpisz ustawiającego lub po prostu zastąpisz metodę pobierającą. Ale jeśli zrobisz jedno i drugie, kompilator tego nie zrozumie i będziesz musiał zsyntetyzować je ręcznie.

Z reguły jednak.

Nie twórz iVars. Wystarczy skorzystać z nieruchomości. Nie syntetyzuj tego.

Fogmeister
źródło
1
Jest więcej przypadków, w których autosynteza nie zostanie wykonana. Sprawdź moją odpowiedź.
Gabriele Petronella
@GabrielePetronella, czy możesz zwięźle wymienić te przypadki dla przeciętnego czytelnika?
Dan Rosenstark
@DanRosenstark to nie ma znaczenia dla nikogo, kto teraz programuje. Naprawdę powinieneś używać Swift. A TBH Myślę, że cała sprawa syntezy nie jest już nawet rzeczą w ObjC. Chyba że pracujesz na bazie kodu, która ma ponad 5 lat.
Fogmeister
@Fogmeister tak, nadal jest to rzecz w przypadku, gdy wspomnisz w swojej odpowiedzi (gdzie zastępujesz zarówno ustawiającego, jak i pobierającego). A jeśli cel-C nie jest „istotny dla nikogo teraz programującego”, proszę zadzwonić do mojej pracy i powiedzieć im. Poinformuj także Facebooka, Google i Apple, które używają Objective-C wewnętrznie z doskonałym skutkiem.
Dan Rosenstark
Jestem pewien, że to prawda bez Quory, ale i tak: quora.com/…
Dan Rosenstark
1

Synteza właściwości jest wymagana, gdy właściwość jest zadeklarowana w protokole. Nie zostanie automatycznie zsyntetyzowany w interfejsie implementującym.

Leo Natan
źródło
prawda, ale to tylko jeden przypadek. Możesz chcieć rozwinąć więcej.
Gabriele Petronella
@GabrielePetronella Tylko o tej późnej porze przychodzi mi na myśl. =]
Leo Natan
0

Dziękuję za wyjaśnienie. Miałem podobny problem.

@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;

Więc teraz, po ich skomentowaniu, przeszedłem i zastąpiłem każde wystąpienie, na przykład

self.firstAsset Wydaje się, że mógłbym również użyć firstAsset, ale wydaje mi się , że zbyt często tęsknię za „ ”.

Harry McGovern
źródło
-1

Xcode nie wymaga jawnej @synthesizedeklaracji.

Jeśli nie piszesz, @synthesizedziała to tak samo, jak:

@synthesize manager = _manager;

Przykładowy kod mógł być stary. Wkrótce to zaktualizują.

Możesz uzyskać dostęp do swoich właściwości, takich jak:

[self.manager function];

To jest zalecana konwencja firmy Apple. Przestrzegam tego i polecam, abyś to zrobił!

Sam Fischer
źródło
2
Instrukcja [_manager function]NIE uzyska dostępu do właściwości, zamiast tego będzie uzyskiwać bezpośredni dostęp do podstawowego modułu ivar .
CouchDeveloper
@CouchDeveloper Jeśli chcesz się dziwić, powinieneś być precyzyjny. Nieruchomość składa się z kilku rzeczy, w tym z ivar. Więc lepiej powiedzieć, że [_manager function]nie używa metod dostępu do właściwości.
Nikolai Ruhe
@CouchDeveloper - Dzięki za to! Naprawione! :)
Sam Fischer
1
@NikolaiRuhe Masz rację Nikolai, powinienem był być bardziej precyzyjny. ;)
CouchDeveloper