多线程 ViewController 中的 UIWebView
Posted
技术标签:
【中文标题】多线程 ViewController 中的 UIWebView【英文标题】:UIWebView in multithread ViewController 【发布时间】:2010-10-31 00:18:46 【问题描述】:我在视图控制器中有一个 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];
【问题讨论】:
My Solution (uses an NSTimer for the last release) 【参考方案1】:这是一些在主线程上运行 UIKit 元素的代码。如果您正在处理不同的线程并需要运行一段 UIKit 代码,只需将它放在这个 Grand Central Dispatch sn-p 的括号之间。
dispatch_async(dispatch_get_main_queue(), ^
// do work here
);
【讨论】:
【参考方案2】:- (void)dealloc
if(![NSThread isMainThread])
[self performSelectorOnMainThread:@selector(dealloc)
withObject:nil
waitUntilDone:[NSThread isMainThread]];
return;
[super dealloc];
【讨论】:
贴代码的时候最好加上简短的解释【参考方案3】:我有相同的解决方案,其中后台线程是最后一个版本,导致视图控制器的解除分配发生在后台线程中,最终导致相同的崩溃。
上述[[self retain] autorelease]
仍然会导致最终释放发生在后台线程的自动释放池中。 (除非自动释放池中的版本有什么特别之处,否则我很惊讶这会有所作为)。
我发现这是我的理想解决方案,将此代码放入我的视图控制器类中:
- (oneway void)release
if (![NSThread isMainThread])
[self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
else
[super release];
这确保了我的视图控制器类的release
方法总是在主线程上执行。
我有点惊讶,某些只能从主线程正确释放的对象还没有内置这样的东西。哦,好吧...
【讨论】:
这结束了我的头疼问题。谢谢! 很高兴我找到了这个 - 2011 年 4 月 非常感谢。你节省了我的时间 我可以把self performSelectorOnMainThread:
改成super performSelectorOnMainThread:
吗?
当然。改成 super 是有道理的 - 不错的收获!【参考方案4】:
我试过了:
[self retain];
[self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
这似乎效果更好。
【讨论】:
【参考方案5】:我尝试了上面发布的两种解决方案,[operationQueue cancelAllOperations]
和 [[self retain] autorelease]
。但是,通过快速单击,仍然存在保留计数降至 0 并且类在辅助线程上被释放的情况。为了避免崩溃,我现在将以下内容放在我的dealloc
中:
if ([NSThread isMainThread])
[super dealloc];
这是一个明显的泄漏,但似乎是两害相权取其轻。
欢迎遇到此问题的任何人提供任何其他见解。
【讨论】:
【参考方案6】:我目前在我的应用程序中遇到了类似的问题。显示 UIWebView 的视图控制器被推送到导航控制器并启动后台线程以检索数据。如果您在线程完成之前点击后退按钮,应用程序将崩溃并显示相同的错误消息。
问题似乎是NSThread
保留了目标(自身)和对象(参数),并在方法运行后释放它——不幸的是,它从线程内释放了两者。因此,当创建控制器时,保留计数为 1,当线程启动时,控制器的保留计数为 2。当您在线程完成之前弹出控制器时,导航控制器释放控制器,从而导致保留计数为 1。到目前为止,这很好——但是如果线程最终完成,NSThread
将释放控制器,这导致保留计数为 0 并从线程内立即释放。这使得 UIWebView(在控制器的 dealloc 方法中释放)引发该线程警告异常并崩溃。
我通过使用[[self retain] autorelease]
作为线程中的最后一条语句(就在线程释放其池之前)成功地解决了这个问题。这确保了控制器对象不会立即释放,而是标记为自动释放并稍后在主线程的运行循环中释放。然而,这是一个有点肮脏的黑客,我宁愿找到一个更好的解决方案。
【讨论】:
您的“变通”解决方案对我有用,谢谢。顺便说一句,Tao,您对使用哪种方法的最后决定是什么? 实际上你正在泄漏内存...!!【参考方案7】:根据您的代码,我不确定具体发生了什么,但看起来 viewDidAppear 被调用并创建了第二个线程,然后您离开控制器并释放它,然后第二个线程完成并在释放的对象“self”上调用 performSelectorOnMainThread。我想你可能只需要检查一下释放没有发生吗?
您收到的错误消息表明您正在从第二个线程运行一些 UIKit 代码。 Apple 最近为 UIKit 的线程调用添加了一些检查,我认为您可能只需要重构加载函数以更新主线程上的 UI,而不是从第二个线程调用 UIWebView 函数。
希望有帮助!
【讨论】:
【参考方案8】:一般来说,当使用它们的视图消失时,您应该取消所有后台操作。如:
- (void)viewWillDisappear:(BOOL)animated
[operationQueue cancelAllOperations];
[super viewWillDisappear:animated;
【讨论】:
感谢您的回复。但是我已经添加了这一点,并且似乎总是在辅助线程中调用 dealloc。仍然无法弄清楚原因。 取消后台操作是“正确”的做法。它不能解决问题的原因是cancelAllOperations
不会自动中止已经运行的操作。您需要做的是在您的load
方法中,在线程休眠后,检查操作的isCancelled
属性;如果是这样,请返回而不调用done
。但是,如果您只是在后台线程中暂停,那么更简单的方法是使用performSelector:withObject:afterDelay:
。以上是关于多线程 ViewController 中的 UIWebView的主要内容,如果未能解决你的问题,请参考以下文章