Не довіряйте своїм очам, коли дивитесь на weak вказівники
У нас є дуже старий шматок коду. По суті, це проксі на NSTimer. NSTimer retain-ить проксю замість нашого дорогоцінного View Controller, що показує PDF файл. Код був написаний за часів ручного підрахунку посилань, потім напівавтоматично переведений на ARC і залишений як є. Виявилося, що всі ці роки, з моменту переходу на ARC, він мав баг, який призводив до витоку пам’яті.
Код виглядає приблизно так:
@interface MYAutosaveTimer ()
@property (nonatomic, weak) MYViewController *owner;
@end
@implementation MYAutosaveTimer
- (void)setOwner:(MYViewController *)owner {
if (_owner) {
// інвалідувати та звільнити справжній таймер
}
_owner = owner;
if (_owner) {
// запустити справжній таймер
}
}
@end
Клієнтський код виглядає приблизно так:
- (instancetype)init {
...
_autosaveTimer = [MYAutosaveTimer new];
[_autosaveTimer setOwner:self];
...
}
- (void)dealloc {
...
[_autosaveTimer setOwner:nil];
}
Виглядає жахливо в сучасні дні, але як я й казав – старий код 🤷♂️. Я його вже переписав як слід.
Річ, яка привернула мою увагу, і причина, чому я вирішив про це написати – це те, як це виглядає в LLDB. Ви заходите в метод -setOwner:. Дивитесь на змінні, і _owner виглядає абсолютно свіжим і повним сил вказівником з якоюсь адресою. А потім… ви ніколи не потрапляєте в if (_owner) { }, де ресурси мали б бути звільнені. Вказівник Шрьодінґера – він наче не nil, але він nil.
Отже, мораль така – не довіряйте своїм очам з weak вказівниками на об’єкт, який деалокується. Вказівники це не просто старі добрі адреси в наші дні.