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: Nieużywana i uchwycona jaźń.

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.

Author: Makoto, 2013-10-29

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
 19
Author: Martin R,
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