2009-10-11 15 views
53

Jestem raczej zdezorientowany właściwościami i zmiennymi instancji w Objective-C.Właściwości i zmienne instancji w Objective-C

Jestem w połowie programu Aaro Hillegassa "Cocoa Programming dla Mac OS X" i wszystko jest logiczne. Można by zadeklarować klasę coś takiego:

@class Something; 

@interface MyClass : NSObject { 
    NSString *name; 
    NSArray *items; 

    Something *something; 

    IBOutlet NSTextField *myTextField; 
} 

@property (nonatomic, retain) NSString *name; 
@property (nonatomic, retain) NSArray *items; 
  • Ponieważ inne przedmioty potrzebne do manipulowania naszymi name i items instancji zmiennych, używamy @property/@synthesize wygenerować akcesorów/mutatorów dla nich. W naszej klasie nie używamy akcesorów/mutatorów - bezpośrednio interweniujemy ze zmienną instancji.

  • something to tylko zmienna instancji, której będziemy używać w naszej klasie, a ponieważ nikt inny nie musi jej używać, nie tworzymy dla niej pary akcesorów i mutatorów.

  • Potrzebujemy interakcji z polem tekstowym w naszym interfejsie użytkownika, więc deklarujemy dla niego IBOutlet, łączymy go i gotowe.

Wszystko bardzo logiczne.

Jednak w świecie iPhone'a sprawy wyglądają inaczej. Użytkownicy deklarują właściwości dla każdej zmiennej pojedynczej instancji, deklarują właściwości dla IBOutlets i używają akcesorów/mutatorów do interakcji ze zmiennymi instancji w obrębie klasy (np. Będą pisać [self setName:@"Test"] zamiast name = @"Test").

Dlaczego? Co się dzieje? Czy te różnice są specyficzne dla iPhone'a? Jakie są zalety deklarowania właściwości dla wszystkich zmiennych instancji, deklarowania właściwości dla IBOutlets i używania akcesorów/mutatorów w obrębie własnej klasy?

+1

Dla kogoś w podobnej sytuacji: oprócz poniższych odpowiedzi, patrz http://stackoverflow.com/questions/1221516/does-an-iboutlet-needs- to-be-a-property-synthesized i http://stackoverflow.com/questions/1250518/what-happens-if-i-dont-retain-iboutlet z powodu dlaczego zadeklarowałbyś IBOutlety jako właściwości. –

Odpowiedz

29

W świecie iPhone'a nie ma żadnego garbage collectora. Musisz dokładnie zarządzać pamięcią, licząc referencje. Mając to na uwadze, należy rozważyć różnicę pomiędzy:

name = @"Test"; 

i

self.name = @"Test"; 
// which is equivalent to: 
[self setName: @"Test"]; 

Jeśli bezpośrednio ustawić zmienną instancji, bez uprzedniego rozpatrzenia, stracisz odniesienie do poprzedniej wartości i można Dostosuj jego liczbę zatrzymań (powinieneś mieć ręcznie określoną wartość). Jeśli uzyskasz dostęp do niego za pośrednictwem właściwości, zostanie on automatycznie obsłużony, wraz z inkrementacją liczby zatrzymań nowo przydzielonego obiektu.

Podstawowa koncepcja nie jest specyficzna dla iPhone'a, ale staje się kluczowa w środowisku bez funkcji czyszczenia pamięci.

+0

... ale w "Programowaniu Cocoa dla Mac OS X", Aaron Hillegass nie używa Garbage Collectora ... zarządzamy pamięcią ręcznie ... –

+3

Steve: Nie widziałem przewodnika. W każdym razie, jeśli nie używasz właściwości i przypisujesz zmienne instancji * w dowolnym miejscu *, niezależnie od tego, czy jest to klasa, czy nie, musisz uważać na liczniki odwołań. Poza tym, systemy Mac OS X były starsze niż odpowiedniki iPhone'a. Objective-C 2.0 był już dostępny, gdy pojawił się iPhone SDK, co spowodowało większe wykorzystanie właściwości niż przy użyciu narzędzi z OS X. Ogólnie rzecz biorąc, jest to głównie kwestia stylu, a nie ścisłej zasady. –

+0

@Mehrdad: OK, to ma sens. Dzięki! –

6

Właściwości są używane do generowania akcesorów dla zmiennych instancji, nie ma magicznych zdarzeń.

Możesz wprowadzić te same akcesoria ręcznie.

W książce Aarona Hillegassa można znaleźć przykłady 3 strategii zarządzania pamięcią dla zmiennych członkowskich. Są to assign/copy/retain. Wybierz jedną z wymaganych dla danej zmiennej.

Zakładam zrozumieć zarządzanie pamięcią w Objective-C ...

Akcesory ukryć złożoność i różnice w zarządzaniu pamięcią dla każdej zmiennej.

Na przykład:

name = @"Test" 

jest proste zadanie, name teraz posiada odniesienie do NSString @"Test". Jednak możesz zdecydować się na użycie copy lub retain. Bez względu na to, która wersja zarządzania pamięcią wybrał akcesor ukrywa złożoność i zawsze dostęp do zmiennej z (lub podobny):

[self setName:@"Test"] 
[self name] 

Teraz setName: może użyć assign/copy or retain i nie musisz się o to martwić.

Domyślam się, że samouczki iPhone'a wykorzystują właściwości, aby ułatwić nowym programistom przechodzenie przez zarządzanie pamięcią (nawet jeśli jest to przydatne do generowania odpowiednich akcesorów z właściwościami, a nie zaimplementowania ich ręcznie za każdym razem).

+0

OK, to ma również sens. Dzięki! –

3

Jednak w świecie iPhone'a sprawy wyglądają inaczej. Użytkownicy deklarują właściwości dla każdej zmiennej pojedynczej instancji, deklarują właściwości dla IBOutlets i używają akcesorów/mutatorów do interakcji ze zmiennymi instancji w klasie (np. Będą pisać [self setName:@"Test"] zamiast name = @"Test").

To nie jest specyficzne dla telefonu iPhone. Poza metodami init i metodą dealloc, dobrą praktyką jest zawsze używać swoich akcesorów. Główną korzyścią, szczególnie na Macu (z Wiązaniami Kakaowymi), jest to, że używanie swoich akcesorów oznacza bezpłatne powiadomienia KVO.

Powodem, dla którego ludzie "deklarują właściwości dla każdej zmiennej pojedynczej instancji" jest prawdopodobnie to, że wszystkie ich zmienne instancji są obiektami, które chcą eksponować jako właściwości. Gdyby mieli coś, co chcieliby zachować prywatność, nie zadeklarowaliby dla niego właściwości w pliku nagłówkowym. (Mogą jednak utworzyć dla niego właściwość w rozszerzeniu klasy w pliku implementacyjnym, aby uzyskać wymienione powyżej bezpłatne powiadomienia KVO.)

Deklarowanie właściwości gniazdek to przesada, według mnie. Nie widzę sensu tego. Jeśli nie zostanie utworzona właściwość, program ładujący nib ustawi wylot przez bezpośredni dostęp do instancji, co jest w porządku dla tego zadania.

+0

Zgodnie z http://developer.apple.com/mac/library/documentation/Cocoa/Conoa/Consumer/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051-CH4-SW6, jeśli nie masz zachowaj obiekty najwyższego poziomu po załadowaniu stalówki na iPhone'a, te obiekty zostaną zwolnione. Na OS X najwyższy poziom obiektów pulpitu ma wartość zatrzymania równą 1. Jednak użycie właściwości (lub co najmniej seterów/modułów pobierających) sprawia, że ​​to, co się dzieje, jest bardziej przejrzyste, a szczególnie dobry pomysł na iPhone'a. –

+0

Zgadzam się, że powoduje to wyraźne zachowanie, ale w tym przypadku nie uważam tego za konieczne - pamiętaj tylko, że punkt sprzedaży jest zawsze powiązaniem posiadania. –

+0

@Peter Hosey: Dzięki! Teraz wszystko zaczyna mieć sens. Jedno pytanie: dlaczego nie powinieneś używać swoich metod dostępu/mutatora w metodzie 'init'? –

2

Sugerowałbym, że nowoczesny rozwój podjął bardzo silną próbę zidentyfikowania, zdefiniowania i zastosowania najlepszych praktyk.

Wśród tych najlepszych praktyk znajdujemy ciągłość i spójność.

Oprócz kłócą zastosowanie dostępowych w init i dealloc metod dostępowe powinny być użyte stałe (wewnątrz i na zewnątrz tych klas) do korzyści, jakie oferują oraz encapsulation polimorficzne implementacjach var (które zezwalają na abstrahowanie i refaktoryzacja) oraz w celu ułatwienia tych najlepszych praktyk ciągłości i spójności. Podstawowe zalety języka zorientowanego na obiekt wchodzą w grę, gdy robimy to w ten sposób i wykorzystujemy pełnię możliwości języka. Ciągła zgodność w kodowaniu jest często niewymienną korzyścią, o czym zwykle świadczy każdy starszy programista.

0

Możesz napisać jak to

//MyClass.h 

@class Something; 

@interface MyClass : NSObject 

@property (nonatomic, strong) NSString *name; 
@property (nonatomic, strong) NSArray *items; 

@end 

//MyClass.m 
@interface MyClass() 

@property (nonatomic, strong) IBOutlet NSTextField *myTextField; 
@property (nonatomic, strong) Something *something; 

@end 
Powiązane problemy