Общая проблема
До сих пор я всегда думал, что 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
, но это, похоже, не так.