Iphone 从后台线程更新解除分配的UIWebView
(根据问题的原始内容进行了两次编辑,两次编辑都从根本上修改了问题。不要挂在第一部分上——虽然这对上下文有用,但我几乎排除了我所问的原始问题。) 正如你从标题中所看到的,我把自己安排在一个角落里,我有一些事情对我不利 在管理大型复杂视图的UIViewController子类中。其中的一部分是UIWebView,它包含我必须构建和执行的web请求的输出,并从中手动组装HTML。由于运行需要一两秒钟,我通过调用Iphone 从后台线程更新解除分配的UIWebView,iphone,memory-management,core-location,Iphone,Memory Management,Core Location,(根据问题的原始内容进行了两次编辑,两次编辑都从根本上修改了问题。不要挂在第一部分上——虽然这对上下文有用,但我几乎排除了我所问的原始问题。) 正如你从标题中所看到的,我把自己安排在一个角落里,我有一些事情对我不利 在管理大型复杂视图的UIViewController子类中。其中的一部分是UIWebView,它包含我必须构建和执行的web请求的输出,并从中手动组装HTML。由于运行需要一两秒钟,我通过调用self-performSelectorInBackground:将其放到后台。然后从我在那
self-performSelectorInBackground:
将其放到后台。然后从我在那里调用的方法中,我使用self-performSelectorOnMainThread:
返回线程堆栈的表面,用我刚刚得到的更新UIWebView
像这样(我将其删节以仅显示相关问题):
这一切都非常好,除非我在CLLocationManager点击其委托方法之前退出了这个视图控制器。如果在我离开此视图后发生这种情况,我会得到:
2010-06-07 16:38:08.508 EverWondr[180:760b] bool _WebTryThreadLock(bool), 0x1b6830: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
不管怎么说,当我过早退出时,我可以重复导致这次崩溃。我根本不相信从后台线程尝试UI更新是真正的问题;我想是我的UIWebView被取消分配了。我怀疑我只是在一个后台线程中,这让运行时怀疑这有什么问题,但我相当肯定不是这样
那么,当我退出该视图时,我如何告诉CLLocationManager不要担心它呢?我在ViewWillEnglishe方法中尝试了[self.locationManager StopUpdateLocation]
,但没有成功
(顺便说一句,MapQuest的API太棒了。比谷歌提供的任何东西都要好。我对它们的推荐度太高了。)
编辑1:
这变得更奇怪了。我实际上已经隔离了它崩溃的地方,它位于-(void)dealoc
的底部。如果我注释掉[super dealloc]
,我不会崩溃!但我似乎无法“进入”[super dealoc]
查看超类中发生了什么,它只是崩溃并停止与我说话
这里有一个stacktrace:
#0 0x3018c380 in _WebTryThreadLock ()
#1 0x3018cac8 in _WebThreadAutoLock ()
#2 0x3256e4f0 in -[UITextView dealloc] ()
#3 0x323d7640 in -[NSObject release] ()
#4 0x324adb34 in -[UIView(Hierarchy) removeFromSuperview] ()
#5 0x3256e4a8 in -[UIScrollView removeFromSuperview] ()
#6 0x3256e3e4 in -[UITextView removeFromSuperview] ()
#7 0x324ffa24 in -[UIView dealloc] ()
#8 0x323d7640 in -[NSObject release] ()
#9 0x3256dd64 in -[UIViewController dealloc] ()
#10 0x0000c236 in -[EventsDetailViewController dealloc] (self=0x1c8360, _cmd=0x3321ff2c) at /Users/danielray/Documents/Xcode Projects/EverWondr svn/source/obj-c/Classes/EventsDetailViewController.m:616
#11 0x323d7640 in -[NSObject release] ()
#12 0x336e0352 in __NSFinalizeThreadData ()
#13 0x33ad8e44 in _pthread_tsd_cleanup ()
#14 0x33ad8948 in _pthread_exit ()
#15 0x33731f02 in +[NSThread exit] ()
#16 0x336dfd2a in __NSThread__main__ ()
#17 0x33ad8788 in _pthread_body ()
#18 0x00000000 in ?? ()
另外,现在很清楚,当运行[self-getDirectionsFromHere]
时,启动“后退”按钮时会发生错误。当我们等待stringWithContentsOfURL
调用返回时,将显示该窗口
编辑2:
好的,注意到堆栈顶部附近的
[UITextView dealloc]
,我意识到我在这个视图层次结构上只有一个UITextView,所以我在-(void)dealloc
中注释了UITextView对象的发布。下一次崩溃时,跟踪点出现了[UIWebView dealloc]
。陛下改变!所以我在dealloc方法中注释了我的one webView版本。。。现在我没有崩溃。这不是一个解决方案,因为据我所知,我现在正在泄漏这两个对象,但它确实很有趣。或者至少,我希望对某人是这样,因为我完全不知所措。我认为你的诊断可能是错误的,但让我们暂时想象一下,事实并非如此,看看你会怎么做
如果问题是在解除分配后访问web视图,以响应上面显示的调用序列,那么该调用将来自updateDirectionsWithHtml:
,通过self.directionsWebView
中的引用。在这种情况下,您应该确保不保留过时的指针,而是将其设置为nil
。然而,我认为你已经这样做了,所以这不太可能是问题所在
如果问题是位置管理器调用委托方法在解除分配后访问控制器,那么您应该确保首先断开链接。类似于[locationManager setDelegate:nil]
的东西应该停止不需要的调用,即使位置管理器本身比控制器寿命长。但是,我怀疑你已经在控制器死亡之前释放了位置管理器,这也不是问题所在
在这种情况下,问题最有可能出现在其他地方,并且可能值得从表面上考虑错误消息:是否可能在某个地方您正在从后台线程调用UIKit?说运行时对此感到困惑,因为您之前在后台线程中,这对我来说似乎有点可疑。这个错误看起来是来自某个相当具体的地方。这个地方可能有问题,但通常最好先假设操作系统比自己的代码工作得更好
如果看不到代码的其余部分,就很难知道这个真正的问题(如果它确实存在的话)可能在哪里。脑海中浮现的一种可能性是,您可能会对某个属性进行一些看似无辜的使用,结果却在某个地方击中了UI;但我承认这只是空泛的猜测
更新:
如果将后台调用放在前台会阻止您点击后退按钮,那么耗时的操作必须已经在进行中,这意味着您的委托方法必须已经被调用——告诉您这样做的日志记录无法通过
我在这里想到的一件事是,performSelectorInBackground
和performSelectorOnMainThread
在选择器完成运行之前保留其接收器。因此,你的控制器释放的时间很可能因此被搞砸了(除非你在某个地方过度释放了它)
从堆栈跟踪来看,它看起来几乎像是从后台线程中调用dealloc
,作为其自身清理的一部分。这就叫
#0 0x3018c380 in _WebTryThreadLock ()
#1 0x3018cac8 in _WebThreadAutoLock ()
#2 0x3256e4f0 in -[UITextView dealloc] ()
#3 0x323d7640 in -[NSObject release] ()
#4 0x324adb34 in -[UIView(Hierarchy) removeFromSuperview] ()
#5 0x3256e4a8 in -[UIScrollView removeFromSuperview] ()
#6 0x3256e3e4 in -[UITextView removeFromSuperview] ()
#7 0x324ffa24 in -[UIView dealloc] ()
#8 0x323d7640 in -[NSObject release] ()
#9 0x3256dd64 in -[UIViewController dealloc] ()
#10 0x0000c236 in -[EventsDetailViewController dealloc] (self=0x1c8360, _cmd=0x3321ff2c) at /Users/danielray/Documents/Xcode Projects/EverWondr svn/source/obj-c/Classes/EventsDetailViewController.m:616
#11 0x323d7640 in -[NSObject release] ()
#12 0x336e0352 in __NSFinalizeThreadData ()
#13 0x33ad8e44 in _pthread_tsd_cleanup ()
#14 0x33ad8948 in _pthread_exit ()
#15 0x33731f02 in +[NSThread exit] ()
#16 0x336dfd2a in __NSThread__main__ ()
#17 0x33ad8788 in _pthread_body ()
#18 0x00000000 in ?? ()
- (void)dealloc {
locationManager.delegate = nil;
[locationManager stopUpdatingLocation];
[locationManager release];
// ...
[super dealloc];
}
[pool drain];
[pool release];
@interface MyAppViewController : UIViewController {
MyAppLocationUpdater *locationUpdater;
}
@property MyAppLocationUpdater* locationUpdater;
@end
@implementation MyAppViewController
@synthesize locationUpdater;
-(void)alloc
{
[super alloc];
locationUpdater = [[MyAppLocationUpdator alloc] init];
locationUpdater.viewController = self;
}
-(void)dealloc
{
locationUpdater.viewController = nil;
[locationUpdater release];
}
-(void)locationManager:(CLLocationManager*)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
[manager stopUpdatingLocation];
[locationUpdater performSelectorInBackgroundThread: @selector(getDirectionsFromHere:) withObject:newLocation];
}
@end
@interface MyAppLocationUpdater : NSObject {
// this reference is not retained or released, the view controller is responsible for clearing the field when it is deallocating
volatile MyAppViewControler *viewController;
}
-(void)getDirectionsFromHere:(CLLocation *)newLocation;
@end
@implementation MyAppLocationUpdater
-(void)getDirectionsFromHere:(CLLocation *)newLocation
{
// same stuff as before except
if (viewController != nil) {
[viewController performSelectorOnMainThread:@selector(updateDirectionsWithHtml:) withObject:directionsOutput waitUntilDone:NO];
}
// release pool as before
}
@end