Общая проблема
До сих пор я всегда думал, что self->_ivar эквивалентно _ivar. Сегодня я узнал, что это не совсем так.
См., например, следующий фрагмент кода:
@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
Несмотря на то, что я спрятал исходный self с некоторым NSInteger, также названным self, неявный синтаксис ivar _testIVar все еще находит "оригинальное", тогда как self->_testIVar, очевидно, этого не делает. В последнем случае компилятор правильно жалуется на
Тип ссылки ссылки "NSInteger" (иначе "long" ) не является указателем
В первом случае, однако, он просто работает.
Проблема реального мира
Этот пример может показаться довольно искусственным, но это совсем не так. Например, проект ExtObjC (используется ReactiveCocoa) определяет очень удобные @weakify(var) и @strongify(var), которые помогают избежать захвата self (и других объектов) в блоках, определяя действительно удобный синтаксис (нет необходимости писать нечетные и громоздкие записи __weak typeof(self) weakSelf = self; [...] ^{ __strong typeof(self) strongSelf = weakSelf; [...] } больше). Например:
- (void)someMethod
{
@weakify(self);
dispatch_async(self.someQueue, ^{
@strongify(self);
NSLog(@"self @ %p", self);
}
}
Без @weakify и @strongify блок захватит сильную ссылку на self. С @weakify и @strongify это не так. Таким образом, освобождение self не будет отложено до тех пор, пока блок не будет запущен. Главное преимущество состоит в том, что вам не нужно забывать использовать weakSelf или strongSelf вместо self, потому что скрывается "оригинал" self.
Это очень удобно, ExtObjC реализует @weakify/@strongify, создавая с макросами нечто похожее на следующее:
- (void)someMethod
{
__weak typeof(self) _weakSelf = self;
dispatch_async(self.someQueue, ^{
__strong typeof(self) self = _weakSelf;
NSLog(@"self @ %p", self);
}
}
Достаточно справедливо, что еще лучше, потому что мы можем просто продолжать использовать self, не заимствуя при этом сильную ссылку на self. Однако, как только мы будем использовать неявный синтаксис синтаксиса, сильная ссылка на "оригинал" self будет сохранена!
- (void)someMethod
{
@weakify(self);
dispatch_async(self.someQueue, ^{
@strongify(self); /* compiler warning: Unused variable self here!!! */
NSLog(@"self->_testIVar: %@", _testIVar);
}
}
Разное
При использовании ivars в блоках мы определенно захватываем self. См. Например, этот снимок экрана:
.
Еще одна забавная вещь о скриншоте заключается в том, что предупреждающие сообщения
Неиспользуемая переменная 'self'
и в строке ниже
Захват "я" сильно в этом блоке, вероятно, приведет к циклу сохранения
Вот почему я думаю, что есть две версии self: -)
Вопрос
Реальный вопрос: что именно означает _testIVar? Как найти "оригинальный" self указатель?
Чтобы уточнить (также см. мой снимок экрана): Как отметил @MartinR (что и я думаю), существует некоторая специальная версия self, которая не может быть изменена и используется только для неявного self-ivar -доступ. Это где-то документировано? В основном, где определено, к чему относится неявный self? Кажется, он ведет себя так же, как, например, Java (с this), но с той разницей, что this является зарезервированным ключевым словом, которое вы не можете переопределить.
Вопрос также не в том, как "исправить" его, просто написать self->_testIVar будет то, что я хочу в примере @weakify/@strongify. Это больше, чем я думал, используя @weakify/@strongify, вы не можете совершить ошибку, неявно сильно захватывая self, но это, похоже, не так.