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;
}
Author: Community, 2011-04-26

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.

 12
Author: Kevin Ballard,
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.

 31
Author: ,
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-29 00:09:31