NSViewController - уволить с очисткой памяти
Моя среда - Yosemite 10.10.5 с Xcode 7.2, использующим ARC.
В простой тестовой программе я пытаюсь различными способами отклонить NSViewController
и все они показывают проблемы с обработкой памяти.
В моем основном контроллере представления у меня есть следующий код. (Части уведомлений предназначены для проверки различных способов отклонения представленного контроллера.)
- (IBAction)showFirstReplacement:(id)sender {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissWithNotification:) name:@"removeFirst" object:nil];
NSStoryboard *sb = [self storyboard];
FirstReplacement *controller = [sb instantiateControllerWithIdentifier:@"first_replacement"];
[self presentViewControllerAsSheet:controller];
}
- (void)dismissWithNotification:(NSNotification *)notification {
NSViewController *controller = [notification object];
[self dismissViewController:controller];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
внутри FirstReplacement
, Я имею:
- (IBAction)dismiss:(id)sender {
[self dismissViewController:self];
// [[NSNotificationCenter defaultCenter] postNotificationName:@"removeFirst" object:self];
// [[self presentingViewController] dismissViewController:self];
}
Раскомментирование любой из трех строк в этом методе дает правильные визуальные результаты, но.... В зависимости от того, какие из вызовов я включаю внутри dismiss:
Я получаю разные результаты при профилировании. С помощью self dismissViewController:
Я не вижу утечек, но FirstReplacement
объекты не освобождаются. Использование любого из двух других подходов избавляет от уволенных FirstReplacement
но протекает один 16-байтовый блок malloc и один NSMutableArray
каждый раз, когда контроллер представления отклоняется.
Согласно инструментам, утечки связаны с методом, называемым [NSViewController _addPresentedViewController:]
,
Есть ли другие меры по очистке, необходимые для предотвращения этих утечек (или раздувания памяти в случае отсутствия утечек)?
1 ответ
Контроллер представления, который представляет другой контроллер представления, также ответственен за отклонение этого. Таким образом, ни одна из строк в методе dismiss в FirstReplacement не является правильной. Вместо этого вы должны создать делегата в FirstReplacement, чтобы он мог уведомить своего делегата (основной контроллер представления) о том, что он должен быть отклонен.
FirstReplacement.h
@class FirstReplacement;
@protocol FirstReplacementDelegate <NSObject>
- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller;
@end
@interface FirstReplacement : NSViewController
@property (nonatomic, weak) id<FirstReplacementDelegate> delegate;
@end
FirstReplacement.m
- (IBAction)dismiss:(id)sender {
[self.delegate firstReplacementShouldDismiss:self];
}
Тогда в вашем основном контроллере представления:
- (IBAction)showFirstReplacement:(id)sender {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissWithNotification:) name:@"removeFirst" object:nil];
NSStoryboard *sb = [self storyboard];
FirstReplacement *controller = [sb instantiateControllerWithIdentifier:@"first_replacement"];
controller.delegate = self;
[self presentViewControllerAsSheet:controller];
}
- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller {
[self dismissViewController:controller];
}
Хотя может показаться, что отправка уведомления такая же, как и у делегата, это не так. Разница в том, что при запуске dismissWithNotification вы все равно выполняете код из FirstReplacement::dismiss. NSNotificationCenter::postNotificationName не завершает выполнение, пока все наблюдатели не завершат выполнение своих селекторов. Таким образом, даже несмотря на то, что код отклонения выполняется в основном контроллере представления, он все еще выполняется из метода dismiss.
Если вы все еще не уверены, переопределите FirstReplacement::dealloc, чтобы напечатать оператор журнала. Вы увидите, что dealloc не вызывается ни одним из ваших методов, но будет вызываться с помощью делегирования.