Iphone 从后台线程更新解除分配的UIWebView

Iphone 从后台线程更新解除分配的UIWebView,iphone,memory-management,core-location,Iphone,Memory Management,Core Location,(根据问题的原始内容进行了两次编辑,两次编辑都从根本上修改了问题。不要挂在第一部分上——虽然这对上下文有用,但我几乎排除了我所问的原始问题。) 正如你从标题中所看到的,我把自己安排在一个角落里,我有一些事情对我不利 在管理大型复杂视图的UIViewController子类中。其中的一部分是UIWebView,它包含我必须构建和执行的web请求的输出,并从中手动组装HTML。由于运行需要一两秒钟,我通过调用self-performSelectorInBackground:将其放到后台。然后从我在那

(根据问题的原始内容进行了两次编辑,两次编辑都从根本上修改了问题。不要挂在第一部分上——虽然这对上下文有用,但我几乎排除了我所问的原始问题。)

正如你从标题中所看到的,我把自己安排在一个角落里,我有一些事情对我不利

在管理大型复杂视图的UIViewController子类中。其中的一部分是UIWebView,它包含我必须构建和执行的web请求的输出,并从中手动组装HTML。由于运行需要一两秒钟,我通过调用
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