Сообщение, отправленное на освобожденный экземпляр
Фон:
Все мои методы OpenTok находятся в одном ViewController
, который попадает в поле зрения, как типичные отношения VC. Деталь VC соединяет вас с другой комнатой в зависимости от вашего выбора. Когда я нажимаю кнопку "Назад", чтобы отобразить представление, я получаю сбой (возможно, 1 из 7 раз):
[OTMessenger setRumorPingForeground] message sent to deallocated instance xxxxx
или
[OTSession setSessionConnectionStatus:]: message sent to deallocated instance 0x1e1ee440
Я помещаю свои методы публикации/отключения в viewDidDisappear:
-(void)viewDidDisappear:(BOOL)animated{
//dispatch_async(self.opentokQueue, ^{
[self.session removeObserver:self forKeyPath:@"connectionCount"];
if(self.subscriber){
[self.subscriber close];
self.subscriber = nil;
}
if (self.publisher) {
[self doUnpublish];
}
if (self.session) {
[self.session disconnect];
self.session = nil;
}
//});
[self doCloseRoomId:self.room.roomId position:self.room.position];
}
Вот трассировка:
![Image]()
Вот DetailViewController на Github: ссылка здесь
Как воспроизвести:
-
Сделайте выбор из MasterVC, который приведет вас к DetailVC, который сразу же пытается подключиться к сеансу и опубликовать
-
Вернитесь к предыдущему, MasterVC быстро, обычно до того, как у сеанса была возможность опубликовать поток
-
Попробуйте это несколько раз, и в конце концов он сработает.
-
Если я замедляю и позволяю издателю возможность подключаться и публиковать, он вряд ли вызовет сбой.
Ожидаемый результат:
Он должен просто отключиться от сеанса/отменить публикацию и начать новый сеанс, когда я иду туда и обратно между Master/DetailVC.
Другое:
Какое у вас устройство и версия ОС?
iOS 6
В какой тип подключения вы были?
Wi-Fi
Зомби включены?
Да
Включено ARC?
Да
Делегаты установлены на нуль?
Да, насколько я знаю
Любая помощь, решающая этот крах, будет с благодарностью оценена. Возможно, мне не хватает чего-то основного, которого я просто не вижу.
Похоже, что объект OTSession в библиотеке OpenTok продолжает отправлять сообщения объектам в этой библиотеке, которые с тех пор были освобождены путем переключения представлений. В библиотеке есть метод [session disconnect], который отлично работает, если вы даете ему достаточно времени, но занимает около 2-3 секунд, и это длительное время, чтобы приостановить приложение между представлениями.
Это может быть глупый вопрос, но:
Есть ли способ остановить все процессы, инициированные определенным VC?
Ответы
Ответ 1
Закрытие сеанса из viewWillDisappear()
работает, если вы можете точно определить, что представление будет вытолкнуто, а не нажато или скрыто. В некоторых ответах предлагается ввести этот код в dealloc()
. Что касается этих предложений, Apple говорит,
Вы должны стараться избегать управления временем жизни ограниченных ресурсов с помощью dealloc.
Итак, вот как вы можете точно определить, что ваше представление будет всплыло. viewWillDisappear()
вызывается, когда представление выставляется из стека или иным образом перемещается в другое место. Это самый простой способ определить, какие, а затем не публиковать/отключать, если он действительно выскочил. Вы можете проверить это с помощью isMovingFromParentViewController
. Кроме того, здесь вы можете удалить конкретных наблюдателей.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]
// This is true if the view controller is popped
if ([self isMovingFromParentViewController])
{
NSLog(@"View controller was popped");
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self.session];
...
//dispatch_async(self.opentokQueue, ^{
if(self.subscriber){
[self.subscriber close];
self.subscriber = nil;
}
if (self.publisher) {
[self doUnpublish];
}
if (self.session) {
[self.session disconnect];
self.session = nil;
}
//});
[self doCloseRoomId:self.room.roomId position:self.room.position];
}
else
{
NSLog(@"New view controller was pushed");
}
}
Ссылка: Тестирование конкретных видов переходов просмотра
Ответ 2
Похоже, OpenTok
имеет ошибку с использованием NSNotificationCenter
внутри классов OTSession
и OTMessenger
. Вы можете видеть, что эти классы в стеке вызовов разделены вызовами NSNotificationCenter
:
![enter image description here]()
Вы можете вручную отказаться от подписки на свой объект OTSession
, когда dealloc (надеюсь, OpenTok
использует defaultCenter
):
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self.session];
}
Вам нужно проверить, действительно ли этот код (dealloc
) выполнен. Если нет - вам нужно исправить проблему освобождения UIViewController
. Многие другие ответы содержат советы о том, как помочь UIViewController
освободиться.
Ответ 3
-(void)viewDidDisappear:(BOOL)animated
вызывается всякий раз, когда вид скрыт, а не только когда он выталкивается из стека представлений.
Итак, если вы нажмете на него представление, viewWillDisappear
будет вызван и ваши объекты будут удалены.
Это особенно проблематично, если вы загружаете те же объекты из viewDidLoad:
вместо viewDidAppear:
.
Возможно, вы должны поместить свой файл unublish/disconnect в -(void)dealloc
.
Ответ 4
Вот что Apple предлагает:
-(void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Но это только крайнее средство для удаления наблюдателей, но часто хорошая привычка всегда добавлять его, чтобы убедиться, что все отключено на dealloc, чтобы предотвратить сбои.
По-прежнему рекомендуется удалить наблюдателя, как только объект больше не готов (или не требуется) для получения уведомлений.
Ответ 5
Я большую часть времени ставил такой код в viewWillDisappear, но я думаю, это не имеет большого значения.
Я считаю, что проблема заключается в том, что ваш делегат сеанса не настроен на ноль. Просто добавьте следующее в свой видDidDisappear:
self.session.delegate=nil;
Ответ 6
Вы должны вызвать [super viewDidDisappear: animate]; в начале. Может быть, это исправит вашу проблему.
И улучшите очистку сеанса и подписчика в методе dealloc:
- (void) dealloc {
[self.session removeObserver:self forKeyPath:@"connectionCount"];
if(self.subscriber){
[self.subscriber close];
self.subscriber = nil;
}
if (self.publisher) {
[self doUnpublish];
}
if (self.session) {
[self.session disconnect];
self.session = nil;
}
[self doCloseRoomId:self.room.roomId position:self.room.position];
//[super dealloc]; //for non-ARC
}
Ответ 7
В соответствии с трассировкой стека, которую вы отправили, центр уведомлений обращается к экземпляру OTSession, который все еще жив. Впоследствии этот экземпляр вызывает методы вызова при сбое на освобожденных объектах.
Добавляя к этому два разных сообщения освобожденного экземпляра, мы знаем, что происходят асинхронные события, возникающие после смерти некоторых объектов, которые вызывают случайный сбой, который у вас есть.
Как было предложено ggfela, вы должны удостовериться, что вы отключили делегатов, которые вы подключили к инфраструктуре OpenTok. Я настоятельно рекомендую вам сделать это в методе dealloc, так как мы хотим убедиться, что после этого момента никто не имеет каких-либо оборванных ссылок на ваш объект:
- (oneway void)dealloc
{
self.session.delegate = nil;
self.publisher.delegate = nil;
self.subscriber.delegate = nil;
}
Еще одна особенность кода заключается в том, что ваш обработчик для sessionDidConnect:
создает новый dispatch_queue каждый раз, когда он вызывается для вызова doPublish:. Это означает, что у вас есть параллельные потоки, совместно использующие экземпляр SROpenTokVideoHandler, что делает его склонным к условиям гонки.