发送到已释放实例的消息
Posted
技术标签:
【中文标题】发送到已释放实例的消息【英文标题】:Message sent to a deallocated instance 【发布时间】:2013-08-15 22:01:35 【问题描述】:背景:
我所有的 OpenTok 方法都在一个 ViewController
中,它会被推送到视图中,就像典型的主/从 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:link here
如何重现:
从 MasterVC 中进行选择,这会将您带入 DetailVC,它会立即尝试连接到会话并发布
快速返回上一个,MasterVC,通常在会话有机会发布流之前
多试几次,最终会崩溃。
如果我放慢速度并让发布者有机会连接和发布,则不太可能导致崩溃。
预期结果:
当我在 Master/DetailVC 之间来回切换时,它应该只是断开会话/取消发布并开始一个新会话。
其他:
您的设备和操作系统版本是什么? ios 6
您使用的是哪种类型的连接? 无线网络
僵尸启用? 是的
ARC 已启用? 是的
代表设置为零? 是的,据我所知
任何解决此崩溃的帮助将不胜感激。也许我错过了一些我看不到的基本内容。
似乎发生的情况是 OpenTok 库中的 OTSession 对象继续向该库中的对象发送消息,这些对象已通过切换视图释放。该库有一个 [session disconnect] 方法,如果你给它足够的时间,它可以正常工作,但它需要接近 2-3 秒,而且在视图之间暂停应用程序的时间很长。
这可能是一个愚蠢的问题,但是: 有没有办法停止某个VC启动的所有进程?
【问题讨论】:
僵尸应该被禁用,如果你检查你的代码中是否有僵尸,你只能使用这个选项。一旦您激活该选项对象将永远不会被释放 @TIMEX Git 存储库抛出 404 @Emin Israfil git repo 的链接不可用。您还在为此寻找答案吗? 您的 github 链接无效。你能给我们另一种方式来查看你的代码吗?另外:你在哪里调用 setRumorPingForeground?你在哪里调用 setSessionConnectionStatus? 我无法查看您的完整代码,如上所述,github 链接不起作用。但是,您似乎根本不应该在视图中管理会话、发布者和订阅者。也许创建一个在交换发生之前保持它的单例模式会更好。或者,如果您需要多个同时存在,则存储在其他地方的对象中。 【参考方案1】:如果您可以确定视图将被弹出,而不是推送或隐藏,则从viewWillDisappear()
关闭会话有效。一些答案建议将此代码放在dealloc()
中。关于这些建议,Apple says,
您应该尝试避免使用 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");
参考:Testing for Specific Kinds of View Transitions
【讨论】:
【参考方案2】:看起来OpenTok
在OTSession
和OTMessenger
类中使用NSNotificationCenter
存在错误。您可以看到调用堆栈中的这些类与 NSNotificationCenter
调用分开:
你可以在dealloc时手动取消订阅OTSession
对象(希望OpenTok
使用defaultCenter
):
- (void)dealloc
[[NSNotificationCenter defaultCenter] removeObserver:self.session];
你需要检查这段代码(dealloc
)是否真的被执行了。如果不是 - 您需要修复 UIViewController
释放的问题。许多其他答案包含如何帮助 UIViewController
被释放的提示。
【讨论】:
【参考方案3】:根据您发布的堆栈跟踪,通知中心会联系到一个仍然存在的 OTSession 实例。之后,此实例会引发对已释放对象的崩溃调用方法。 再加上两条不同的已释放实例消息,我们知道在某些对象死亡后会发生异步事件,这些事件会触发您遇到的随机崩溃。
正如 ggfela 建议的那样,您应该确保将已连接到 OpenTok 框架的代表清零。我强烈建议你在 dealloc 方法中这样做,因为我们要确保在那之后,没有人对你的对象有任何悬空引用:
- (oneway void)dealloc
self.session.delegate = nil;
self.publisher.delegate = nil;
self.subscriber.delegate = nil;
代码中的另一个奇怪之处是,您的 sessionDidConnect:
处理程序在每次被调用时都会创建一个新的 dispatch_queue,以便调用 doPublish:。这意味着您有并发线程共享 SROpenTokVideoHandler 实例,这使其容易出现竞争条件。
【讨论】:
【参考方案4】:这就是Apple suggests:
-(void) dealloc
[[NSNotificationCenter defaultCenter] removeObserver:self];
但这只是删除观察者的最后手段,始终添加它以确保在 dealloc 上清理所有内容以防止崩溃通常仍然是一个好习惯。
remove the observer as soon as the object is no longer ready (or required) to receive notifications 还是个好主意。
【讨论】:
对于 iOS 5 及以下版本,我们必须在dealloc
末尾调用 [super dealloc];
,因为这些版本没有 ARC
你是对的,但是使用 ARC 你可能不会调用 [super dealloc] - 我怀疑很多人仍然支持
只是一个想法!我们必须为此做好准备!【参考方案5】:
-(void)viewDidDisappear:(BOOL)animated
会在视图被隐藏时调用,而不仅仅是在视图堆栈中弹出时调用。
因此,如果您将视图推送到它上面,viewWillDisappear
将被调用并删除您的对象。
如果您从 viewDidLoad:
而不是 viewDidAppear:
加载这些相同的对象,这会特别成问题。
也许您应该将取消发布/断开连接代码放在-(void)dealloc
。
【讨论】:
在dealloc方法中放入及时断开代码是不明智的。该部分用于释放接收器占用的内存。viewDidDisappear
是一个更好的选择,可以测试视图是否弹出。如果是,则断开连接。【参考方案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】:我大部分时间都将这样的代码放在 viewWillDisappear 中,但我想这并不重要。
我认为问题在于您的会话委托未设置为零。只需在 viewDidDisappear 中添加以下内容:
self.session.delegate=nil;
【讨论】:
感谢您的建议,但这并不能解决问题。我的编辑可能会有所帮助。 当会话断开连接时,所有 OTSubscriber 和 OTPublisher 对象的视图都将从其父视图中删除。您是否尝试过仅断开会话连接?不要将订阅者设为 nil,因为会话似乎需要它。以上是关于发送到已释放实例的消息的主要内容,如果未能解决你的问题,请参考以下文章
[UINavigationController 保留]:发送到已释放实例的消息