我在viewcontroller中有一个UIWebView,它有两个方法如下。问题是如果我在第二个线程完成之前弹出(点击导航栏)此控制器,应用程序将在[super dealloc]之后崩溃,因为“试图从主线程以外的线程获取Web锁定或这可能是从辅助线程调用UIKit的结果。“任何帮助都会非常感激。
-(void)viewDidAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(load) object:nil];
[operationQueue addOperation:operation];
[operation release];
}
-(void)load {
[NSThread sleepForTimeInterval:5];
[self performSelectorOnMainThread:@selector(done) withObject:nil waitUntilDone:NO];
}
答案 0 :(得分:35)
我有一个相同的解决方案,其中后台线程是最后一个版本,导致视图控制器的dealloc发生在后台线程中,最终导致相同的崩溃。
上面的[[self retain] autorelease]
仍然会导致最终版本从后台线程的自动释放池中发生。 (除非有关于自动释放池的发布有什么特别之处,我很惊讶这会产生影响)。
我发现这是我理想的解决方案,将此代码放入我的视图控制器类中:
- (oneway void)release
{
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
} else {
[super release];
}
}
这确保了我的视图控制器类的release
方法总是在主线程上执行。
我有点惊讶的是,某些只能从主线程中正确释放的对象还没有内置的东西。哦,好吧......
答案 1 :(得分:12)
这是在主线程上运行UIKit元素的一些代码。如果您正在处理不同的线程并且需要运行一段UIKit代码,请将其放在此Grand Central Dispatch代码段的括号中。
dispatch_async(dispatch_get_main_queue(), ^{
// do work here
});
答案 2 :(得分:1)
通常,当使用它们的视图消失时,您应该取消任何后台操作。如:
- (void)viewWillDisappear:(BOOL)animated {
[operationQueue cancelAllOperations];
[super viewWillDisappear:animated;
}
答案 3 :(得分:1)
我目前在我的应用程序中遇到了类似的问题。显示UIWebView的视图控制器被推送到导航控制器并启动后台线程以检索数据。如果在线程完成之前点击后退按钮,应用程序将崩溃并显示相同的错误消息。
问题似乎是NSThread
保留了目标(self)和object(参数)并在方法运行后释放它 - 不幸的是它从线程中释放。因此,当控制器被创建时,保留计数为1,当线程启动时,控制器获得保留计数2.当在线程完成之前弹出控制器时,导航控制器释放控制器,这导致保留计数1.到目前为止这很好 - 但是如果线程最终完成,NSThread
释放控制器,这导致保留计数为0并且线程内立即释放。这使得UIWebView(在控制器的dealloc方法中发布)引发该线程警告异常并崩溃。
我通过使用[[self retain] autorelease]
作为线程中的最后一个语句(在线程释放其池之前)成功解决了这个问题。这可确保控制器对象不会立即释放,但会在主线程的运行循环中标记为自动释放并取消分配。然而,这是一个有点肮脏的黑客,我宁愿找到更好的解决方案。
答案 4 :(得分:1)
我试过了:
[self retain];
[self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
似乎工作得更好。
答案 5 :(得分:0)
我不确定你的代码到底发生了什么,但看起来viewDidAppear正在调用并创建第二个线程,然后你导航离开控制器并释放它,然后第二个线程结束并在已释放的对象“self”上调用performSelectorOnMainThread。我想您可能只需检查发布是否未发生?
您收到的错误消息暗示您正在从第二个线程运行一些UIKit代码。 Apple最近为UIKit的线程调用添加了一些检查,我想你可能只需要重构你的load函数来更新主线程上的UI,而不是从第二个线程调用UIWebView函数。
希望有所帮助!
答案 6 :(得分:0)
我尝试了上面发布的两个解决方案[operationQueue cancelAllOperations]
和[[self retain] autorelease]
。但是,通过快速单击,仍然存在保留计数降至0并且类在辅助线程上取消分配的情况。为了避免崩溃,我暂时在dealloc
中添加以下内容:
if ([NSThread isMainThread]) {
[super dealloc];
}
这是一个明显的泄漏,但似乎是2个邪恶中的较小者。
欢迎任何遇到此问题的人提供任何其他见解。
答案 7 :(得分:0)
- (void)dealloc
{
if(![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(dealloc)
withObject:nil
waitUntilDone:[NSThread isMainThread]];
return;
}
[super dealloc];
}