Почему UIViewController освобождается в основном потоке?
Я недавно наткнулся наПроблема распределения в некотором коде Objective-C. Эта тема обсуждалась ранее при переполнении стека вBlock_release освобождает объекты пользовательского интерфейса в фоновом потоке, Я думаю, что понимаю проблему и ее последствия, но, конечно, я хотел воспроизвести ее в небольшом тестовом проекте. Я сначала создал свой собственныйSOUnsafeObject
(= объект, который всегда должен быть освобожден в основном потоке).
@interface SOUnsafeObject : NSObject
@property (strong) NSString *title;
- (void)reloadDataInBackground;
@end
@implementation SOUnsafeObject
- (void)reloadDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.title = @"Retrieved data";
});
sleep(3);
});
}
- (void)dealloc {
NSAssert([NSThread isMainThread], @"Object should always be deallocated on the main thread");
}
@end}]
Теперь, как и ожидалось, если я поставлю[[[SOUnsafeObject alloc] init] reloadDataInBackground];
внутриapplication:didFinishLaunching..
приложение вылетает через 3 секунды из-за ошибочного подтверждения. Предлагаемое исправление, похоже, работает. То есть приложение больше не падает, если я изменю реализациюreloadDataInBackground
чтобы:
__block SOUnsafeObject *safeSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
safeSelf.title = @"Retrieved data";
safeSelf = nil;
});
sleep(3);
});
Итак, похоже, что мое понимание проблемы и того, как ее можно решить в рамках ARC, является правильным. Но просто чтобы быть на 100% уверенным .. Давайте попробуем то же самое сUIViewController
(так какUIViewController
вероятно, будет выполнять рольSOUnsafeObject
в реальной жизни). Реализация практически идентичнаSOUnsafeObject
:
@interface SODemoViewController : UIViewController
- (void)reloadDataInBackground;
@end
@implementation SODemoViewController
- (void)reloadDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.title = @"Retrieved data";
});
sleep(3);
});
}
- (void)dealloc {
NSAssert([NSThread isMainThread], @"UI objects should always be deallocated on the main thread");
NSLog(@"I'm deallocated!");
}
@end
Теперь давайте[[SODemoViewController alloc] init] reloadDataInBackground];
внутриapplication:didFinishLaunching..
, Хм, утверждение не ошибается .. СообщениеI'm deallocated!
выводится на консоль через 3 секунды, поэтому я почти уверен, что контроллер представления освобождается.
Почему контроллер представления освобождается в основном потоке, а небезопасный объект освобождается в фоновом потоке? Код практически идентичен. Есть лиUIKit делать какие-то модные вещи за кулисами, чтобы убедиться,UIViewController
всегда освобождается в основном потоке? Я начинаю подозревать это, так как следующий фрагмент также не нарушает мое утверждение:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
SODemoViewController()
});
Если да, документировано ли это поведение где-нибудь? Можно ли полагаться на это поведение? Или я просто совершенно не прав и есть что-то очевидное, что я здесь упускаю?
Заметки: Я полностью осознаю тот факт, что я могу использовать__weak
ссылка здесь, но давайте предположим, что контроллер представления все еще должен быть жив, чтобы выполнить наш код завершения в главном потоке. Кроме того, я пытаюсь понять суть проблемы, прежде чем обойти ее. Я преобразовал код в Swift и получил те же результаты, что и в Objective-C (исправление дляSOUnsafeObject
там синтаксически еще страшнее).