Czy własność prywatna @ tworzy zmienną instancji @ private?
Czytałem, że @synthesize
automatycznie utworzy odpowiednie zmienne instancji dla @property
i że Ivary są @protected
domyślnie. Ale co jeśli użyję rozszerzenia klasy (jak poniżej), aby wskazać, że metody @property
mają być prywatne?
// Photo.m
@interface Photo ()
@property (nonatomic, retain) NSMutableData *urlData;
@end
Czy odpowiedni ivar będzie wtedy @private
? A może powinienem to wyraźnie zadeklarować jako @private
w ten sposób?
// Photo.h
@interface Photo : Resource {
@private
NSMutableData *urlData;
}
2 answers
@private
zmienne instancji są funkcją tylko w czasie kompilacji. Biorąc pod uwagę, że podkłady dla @property
'S są już ukryte, @private
nic nie robi. Więc w istocie, to już @private
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-26 01:33:16
Ellaborating on Kevin ' s answer:
Kiedy deklarujesz klasę, np.:
@interface SomeClass : NSObject {
@public
id publicIvar;
@protected
id protectedIvar;
@private
id privateIvar;
}
@end
Kompilator1 decyduje o układzie zmiennej instancji dla tej klasy. Układ ten określa przesunięcia zmiennych instancji w odniesieniu do adresu instancji tej klasy. Jeden możliwy układ to:
+--> publicIvar address = instance address + offsetOfPublicIvar
|
|
+-----+------------+-----+---------------+-----+-------------+-----+
| ... | publicIvar | ... | protectedIvar | ... | privateIvar | ... |
+-----+------------+-----+---------------+-----+-------------+-----+
|
|
+--> instance address
Gdy zmienna instancji jest odwoływana w kodzie - albo w implementacji klasy, albo w innej części bazy kodu, kompilator zastępuje to odniesienie odpowiednim przesunięciem zmiennej instancji w odniesieniu do adresu odpowiadającej instancji.
Na przykład, w implementacji SomeClass,
privateIvar = someObject;
Lub
self->privateIvar = someValue;
Jest tłumaczone jako coś w rodzaju:
*(self + offsetOfPrivateIvar) = someObject;
Podobnie, poza klasą,
SomeClass *obj = [SomeClass new];
obj->publicIvar = someObject;
Jest tłumaczone jako coś w rodzaju:
SomeClass *obj = [SomeClass new];
*(obj + offsetOfPublicIvar) = someObject;
Jednak kompilator zezwala na to tylko w zależności od widoczności instancji zmienna:
- zmienna instancji prywatnej może być odwołana tylko w implementacji odpowiedniej klasy;
- zmienna chronionej instancji może być odwołana tylko w implementacji odpowiedniej klasy i jej podklas;
- publiczna zmienna instancji może być odwołana w dowolnym miejscu.
Gdy zmienna instancji jest zadeklarowana w rozszerzeniu klasy, np.
@interface SomeClass () {
id extensionIvar;
}
@end
Kompilator dodaje go do zmiennej instancji układ:
+-----+------------+-----+---------------+
| ... | otherIvars | ... | extensionIvar |
+-----+------------+-----+---------------+
I każde odniesienie do tej zmiennej instancji jest zastępowane przez odpowiednie przesunięcie w odniesieniu do instancji. Jednakże, ponieważ ta zmienna instancji jest znana tylko do pliku implementacji, w którym rozszerzenie klasy zostało zadeklarowane, kompilator nie pozwoli innym plikom odwoływać się do niej. Dowolny plik źródłowy może odwoływać się tylko do zmiennych instancji, które zna (zgodnie z regułami widoczności). Jeśli zmienne instancji są zadeklarowane w pliku nagłówkowym importowanym przez plik źródłowy, a następnie plik źródłowy (lub dokładniej kompilator podczas tłumaczenia tej jednostki) jest ich świadomy.
Z drugiej strony, zmienna rozszerzenia jest znana tylko z pliku źródłowego, w którym została zadeklarowana. Możemy więc powiedzieć, że zmienne instancji zadeklarowane w rozszerzeniach klas są ukryte przed innymi plikami. To samo rozumowanie odnosi się do przechowywania zmiennych instancji właściwości zadeklarowanych w rozszerzeniach klas. Jest podobny do @private
, ale bardziej restrykcyjny.
Uwaga, jednak reguły widoczności w czasie wykonywania nie są egzekwowane. Używając kodowania klucz-wartość, dowolny plik źródłowy może czasami (reguły są opisane tutaj) uzyskać dostęp do zmiennej instancji:
SomeClass *obj = [SomeClass new];
id privateValue = [obj valueForKey:@"privateIvar"];
Łącznie ze zmienną instancji zadeklarowaną w rozszerzeniu:
id extensionValue = [obj valueForKey:@"extensionIvar"];
Niezależnie od KVC, dostęp do zmiennych instancji można uzyskać poprzez Runtime API Objective - C:
Ivar privateIvar = class_getInstanceVariable([SomeClass class],
"privateIvar");
Ivar extensionIvar = class_getInstanceVariable([SomeClass class],
"extensionIvar");
id privateValue = object_getIvar(obj, privateIvar);
id extensionValue = object_getIvar(obj, extensionIvar);
Zauważ, że klasa może mieć więcej niż jedno rozszerzenie klasy. Jednak jedno rozszerzenie klasy Nie Można zadeklarować zmiennej instancji o tej samej nazwie co innej zmiennej instancji, włączając w to zmienne instancji zadeklarowane w innych rozszerzeniach klas. Ponieważ kompilator emituje symbole takie jak:
_OBJC_IVAR_$_SomeClass.extensionIvar
Dla każdej zmiennej instancji, posiadanie różnych rozszerzeń deklarujących zmienne instancji o tej samej nazwie nie powoduje błędu kompilatora, ponieważ dany plik źródłowy nie wie o innym pliku źródłowym, ale powoduje błąd linkera.
1ten układ można zmienić przez runtime Objective-C. W rzeczywistości offsety są obliczane przez kompilator i przechowywane jako zmienne, a runtime może je zmieniać w razie potrzeby.
PS: nie wszystko w tej odpowiedzi dotyczy wszystkich wersji kompilatora/runtime. Rozważałem tylko Objective-C 2.0 z nietrwałym ABI i najnowszymi wersjami clang / LLVM.