Подтвердить что ты не робот

Сообщение, отправленное на освобожденный экземпляр

Фон:

Все мои методы 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?

4b9b3361

Ответ 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, что делает его склонным к условиям гонки.