Objective - C self - > Ivar access with explicit vs implicit self ->
Problem Ogólny
Do tej pory zawsze uważałem, że self->_ivar
jest odpowiednikiem _ivar
. Dziś dowiedziałem się, że nie jest to do końca prawda.
Zobacz na przykład następujący fragment kodu:
@interface TestClass : NSObject {
NSString *_testIVar;
}
@end
@implementation TestClass
- (instancetype)init
{
if ((self = [super init])) {
_testIVar = @"Testing Only";
}
return self;
}
- (void)test
{
{
NSInteger self = 42;
NSLog(@"without arrow: %@", _testIVar); /* OK */
NSLog(@"with arrow: %@", self->_testIVar); /* COMPILER ERROR! */
}
}
@end
Chociaż ukryłem oryginał self
z jakimś NSInteger
również nazwanym self
, domyślna składnia ivar _testIVar
nadal znajduje "oryginalne" ja, podczas gdy self->_testIVar
oczywiście nie. W tym drugim przypadku kompilator poprawnie narzeka na
Członek Typ odniesienia "nsinteger" (aka "long") nie jest wskaźnikiem
W pierwszym przypadku jednak, to po prostu działa.
Realny Problem
Ten przykład może wydawać się raczej sztuczny, ale wcale nie jest. Na przykład projekt ExtObjC (używany przez Reactivecoa ) definiuje bardzo poręczne @weakify(var)
i @strongify(var)
, które pomagają w silnym przechwytywaniu self
(i innych obiektów) w blokach, definiując naprawdę poręczną składnię (nie trzeba pisać nieparzystych i nieparzystych kłopotliwe do napisania __weak typeof(self) weakSelf = self; [...] ^{ __strong typeof(self) strongSelf = weakSelf; [...] }
). Na przykład:
- (void)someMethod
{
@weakify(self);
dispatch_async(self.someQueue, ^{
@strongify(self);
NSLog(@"self @ %p", self);
}
}
BEZ @weakify
i @strongify
, blok przechwyciłby silne odniesienie do self
. Z @weakify
i @strongify
nie. więc dealokacja self
nie zostanie przełożona, dopóki blok nie zostanie uruchomiony. Główną zaletą jest jednak to, że nie musisz pamiętać, aby używać weakSelf
lub strongSelf
zamiast self
, ponieważ "oryginalny" self
jest ukryty.
To bardzo przydatne, ExtObjC implementuje @weakify
/ @strongify
generując coś podobnego jak poniżej z makrami:
- (void)someMethod
{
__weak typeof(self) _weakSelf = self;
dispatch_async(self.someQueue, ^{
__strong typeof(self) self = _weakSelf;
NSLog(@"self @ %p", self);
}
}
W porządku, to nawet lepiej, ponieważ możemy po prostu nadal używać self
bez przechwytywania silnego odniesienia do self
. Jednakże, jak tylko użyjemy implicit-ivars-of-self-składni, silne odniesienie do "oryginału" self
nadal zostanie uchwycone!
- (void)someMethod
{
@weakify(self);
dispatch_async(self.someQueue, ^{
@strongify(self); /* compiler warning: Unused variable self here!!! */
NSLog(@"self->_testIVar: %@", _testIVar);
}
}
Misc
Kiedy używamy ivarów w blokach, zdecydowanie przechwytywamy self
. Zobacz na przykład ten zrzut ekranu:
.
Kolejna zabawa zrzut ekranu jest taki, że komunikaty ostrzegawcze są
Nieużywana zmienna 'self'
I w linii poniżej
W tym samym czasie, w ciągu ostatnich kilku lat, udało nam się osiągnąć sukces.]}
Dlatego myślę, że są dwie wersje self
:-)
Pytanie
Prawdziwe pytanie brzmi: co dokładnie oznacza _testIVar
? Jak znaleźć wskaźnik "oryginalny" self
?
Aby wyjaśnić (Zobacz też mój screenshot): jak zauważył @MartinR (co też myślę), istnieje specjalna wersja self
, której nie można zmienić i jest używana tylko dla implicit-self-ivar-access. Czy to gdzieś udokumentowane? Zasadniczo gdzie jest zdefiniowane, do czego odnosi się implicit self
? Wygląda na to, że zachowuje się tak samo, jak na przykład Java (z this
), ale z tą różnicą, że this
jest zarezerwowanym słowem kluczowym, którego nie można zastąpić.
Pytanie też nie jest jak to "naprawić", tylko napisać self->_testIVar
will be what I want in the @weakify
/@strongify
przykład. To bardziej, że myślałem, używając @weakify
/@strongify
nie można już popełnić błędu w sposób dorozumiany mocno przechwytywania self
, ale to po prostu nie wydaje się mieć miejsca.
1 answers
Wszystkie metody Objective-C są wywoływane z dwoma ukrytymi argumentami (z "Objective-C Runtime Programming Guide"):
- obiekt odbierający
- selektor metody
I metoda może odnosić się do otrzymującego obiektu jako self
(i do własnego selektora jako _cmd
).
Teraz _ivar
jest równoważne self->_ivar
Gdzie self
jest to
parametr funkcji .
Dopóki nie zdefiniujesz nowej zmiennej self
w wewnętrzny zakres, _ivar == self->_ivar
jest prawdziwy.
Jeśli zdefiniujesz nową zmienną self
w wewnętrznym zakresie to masz
- lokalnie zdefiniowane
self
, - "jaźń ukryta", która jest pierwszym parametrem funkcji,
I _ivar
nadal odnosi się do"ukrytej jaźni"! To wyjaśnia ostrzeżenia kompilatora w Twoim bloku, które wydają się ze sobą sprzeczne:
-
"nieużywana zmienna 'self' " odnosi się do lokalnie zdefiniowanej
self
, - "uchwycenie' ja ' silne w tym bloku ..." odnosi się do" implicit self " funkcji.
Poniższy kod również to pokazuje:
@interface MyClass : NSObject
{
NSString *_ivar;
}
@end
@implementation MyClass
- (void)test
{
_ivar = @"foo"; // Set instance variable of receiver
{
MyClass *self = [MyClass new]; // Redefine self in inner scope
self->_ivar = @"bar"; // Set instance variable of redefined self
NSLog(@"%@ - %@", self->_ivar, _ivar);
// Output: bar - foo
}
}
@end
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
2013-10-30 22:22:35