22

背景:

我所有的 OpenTok 方法都在一个ViewController被推送到视图中的方法中,就像典型的 Master/detail VC 关系一样。detailVC 根据您的选择将您连接到不同的房间。当我按下后退按钮以弹出视图时,我遇到了崩溃(可能 7 次中有 1 次):

[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];
}

这是一个跟踪:

图片

这是 Github 上的 DetailViewController:链接在这里

如何重现:

  1. 从 MasterVC 中进行选择,这会将您带入 DetailVC,它会立即尝试连接到会话并发布

  2. 快速返回上一个,MasterVC,通常在会话有机会发布流之前

  3. 尝试多次,最终它会崩溃。

  4. 如果我放慢速度并让发布者有机会连接和发布,则不太可能导致崩溃。

预期结果:

当我在 Master/DetailVC 之间来回切换时,它应该只是断开会话/取消发布并开始一个新会话。

其他:

您的设备和操作系统版本是什么?iOS 6

您使用的是哪种类型的连接?无线上网

僵尸启用?是的

启用 ARC?是的

代表设置为零?是的,据我所知

任何解决此崩溃的帮助将不胜感激。也许我错过了一些我看不到的基本内容。

似乎发生的事情是 OpenTok 库中的 OTSession 对象继续向该库中的对象发送消息,这些对象已通过切换视图释放。该库有一个 [session disconnect] 方法,如果你给它足够的时间,它可以正常工作,但它需要接近 2-3 秒,而且在视图之间暂停应用程序的时间很长。

这可能是一个愚蠢的问题,但是:有没有办法停止某个 VC 启动的所有进程?

4

7 回答 7

4

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");
    }
}

参考: 测试特定类型的视图转换

于 2015-04-18T01:44:38.930 回答
3

看起来在和类内部OpenTok有一个错误。您可以看到调用堆栈中的这些类与调用分开:NSNotificationCenterOTSessionOTMessengerNSNotificationCenter

在此处输入图像描述

OTSession您可以在 dealloc 时手动取消订阅您的对象(希望OpenTok使用defaultCenter):

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self.session];
}

您需要检查此代码 ( dealloc) 是否真正执行。如果不是 - 您需要解决UIViewController释放问题。许多其他答案包含如何帮助UIViewController解除分配的提示。

于 2015-04-18T00:21:07.283 回答
2

这就是苹果的建议

-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
}

但这只是删除观察者的最后手段,始终添加它以确保在 dealloc 上清理所有内容以防止崩溃通常仍然是一个好习惯。

一旦对象不再准备好(或不需要)接收通知,就移除观察者仍然是一个好主意。

于 2015-04-16T20:30:01.923 回答
2

-(void)viewDidDisappear:(BOOL)animated每当视图被隐藏时调用,而不仅仅是当它从视图堆栈中弹出时。

因此,如果您将视图推过它,viewWillDisappear将被调用并删除您的对象。

如果您从而viewDidLoad:不是从viewDidAppear:.

也许您应该将取消发布/断开连接的代码放入-(void)dealloc.

于 2015-04-14T19:58:34.263 回答
1

我大部分时间都将这样的代码放在 viewWillDisappear 中,但我想这并不重要。

我认为问题在于您的会话代表未设置为零。只需在 viewDidDisappear 中添加以下内容:

self.session.delegate=nil;
于 2013-08-14T08:31:02.040 回答
1

你必须调用 [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
}
于 2015-04-14T18:48:41.507 回答
1

根据您发布的堆栈跟踪,通知中心会联系到一个仍然存在的 OTSession 实例。之后,此实例会引发对已释放对象的崩溃调用方法。再加上两条不同的已释放实例消息,我们知道在某些对象死亡后会发生异步事件,这些事件会触发您遇到的随机崩溃。

正如 ggfela 建议的那样,您应该确保将已连接到 OpenTok 框架的代表清零。我强烈建议您在 dealloc 方法中执行此操作,因为我们要确保在那之后,没有人对您的对象有任何悬空引用:

- (oneway void)dealloc
{
    self.session.delegate = nil;
    self.publisher.delegate = nil;
    self.subscriber.delegate = nil;
}

代码中的另一个奇怪的事情是,您的处理程序sessionDidConnect:每​​次调用它时都会创建一个新的 dispatch_queue 以调用 doPublish:。这意味着您有并发线程共享 SROpenTokVideoHandler 实例,这使得它容易出现竞争条件。

于 2015-04-18T06:58:57.260 回答