如何安全地关闭viewWillDisappear中的加载UIWebView?

时间:2009-04-26 00:15:51

标签: iphone cocoa-touch uiwebview

我有一个包含UIWebView的视图,它正在加载谷歌地图(因此有很多javascript等)。我遇到的问题是,如果用户在Web视图加载完之前点击导航栏上的“后退”按钮,我不清楚如何整理地告诉Web视图停止加载然后释放它,而不是获取发送到解除分配的实例的消息。我也不确定Web视图是否喜欢它的容器视图在它完成之前消失(但如果用户在加载之前点击后退按钮,我别无选择。)

在我的viewWillDisappear处理程序中我有这个

map.delegate=nil;
[self.map stopLoading];

这似乎处理大多数情况OK,因为nil'ing委托停止它将didFailLoadWithError发送到我的视图控制器。但是,如果我在视图的dealloc方法中释放Web视图,有时(间歇性地)我仍会收到发送到解除分配的实例的消息,这似乎与在实际页面中运行的javascript有关,例如:

-[UIWebView webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:]: message sent to deallocated instance 0x4469ee0

如果我只是不发布webview,那么我不会收到这些消息,但我想我正在泄漏webview。

如果我没有发送'stopLoading'消息,只是在viewWillDisappear中发布webview,那么我会看到这样的消息:

/SourceCache/WebCore/WebCore-351.9.42/wak/WKWindow.c:250 WKWindowIsSuspendedWindow:  NULL window.

可能相关,我有时(再次完全间歇)得到一个丑陋的heisenbug,点击其他视图的导航栏上的后退按钮将弹出标题,但不是视图。换句话说,我在堆栈上留下了视图n的标题,但是显示的视图仍然是视图n + 1(结果是你被困在这个屏幕上并且无法返回到根视图 - 你可以去其他方向,即推送更多视图并弹回到没有正确弹出的视图,只是不到根视图。唯一的出路是退出应用程序)。在其他时候,相同视图上的推送和弹出的相同序列工作正常。

这个特别令我疯了。我认为这可能与视图在加载Web视图之前消失有关,即在这种情况下我怀疑它可能会在内存上乱写并混淆视图堆栈。或者,这可能是完全不相关的,而且是其他地方的错误(我从来没有能够在调试构建模式下重现它,只有当我无法使用gdb观看时才会发布版本设置:-)。从我的调试运行开始,我认为我不会过度发布任何内容。而且我似乎只能触发它,如果在某些时候我已经点击了具有Web视图的视图,并且在此之后不会立即发生。

6 个答案:

答案 0 :(得分:52)

对此的变化应该解决泄漏和僵尸问题:

- (void)loadRequest:(NSURLRequest *)request
{
    [self retain];
    if ([webView isLoading])
        [webView stopLoading];
    [webView loadRequest:request];
    [self release];
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
    [self retain];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [self release];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [self release];
}

- (void)viewWillDisappear
{
    if ([webView isLoading])
        [webView stopLoading];
}

- (void)dealloc
{
    [webView setDelegate:nil];
    [webView release];
    [super dealloc];
}

答案 1 :(得分:1)

有几种方法可以处理它,但这应该有效。你想要didFailLoadWithError消息,它告诉你它被停止了。

设置标志isLeaving = YES; 向Webview发送stopLoading。

在didFailLoadWithError:中,检查您何时收到错误 webview停止:

if((thiserror.code == NSURLErrorCancelled)&&(isLeaving == YES)){

[otherClass performSelector:@selector(shootWebview)withObject:nil withDelay:0]

}

在shootWebview中发布webView:


变体: 如果你想成为骑士,你可以执行performSelector:withObject:withDelay:延迟[fillintheblank],在没有检查的情况下调用它10-30秒,你几乎肯定会逃脱它,尽管我不喜欢不推荐它。

你可以让didFailLoadWithError设置一个标志并在其他地方清理它。

或者我最喜欢的,也许你不需要在你离开时将它全部解除。你不会再次显示那个视图容器吗?为什么不把它重复使用呢?

您的调试与发布问题不同,您可能需要检查配置以确保它完全相同。赏金是问题的可重复部分,对吧? ; - 。)

- 哦等一下,您可能会使用WebView将整个View容器关闭。您可以对上述内容进行修改,并等待在shootWebView中释放整个容器。

答案 2 :(得分:1)

您在帖子的第二部分中描述的UINavigationController错误可能与您对内存警告的处理有关。我已经经历过这种现象,并且通过在查看堆栈中的视图(n + 1)时模拟内存警告,我已经能够在堆栈中的视图n上重现它。

UIWebView是一个内存消费者,因此当它被用作视图层次结构的一部分时,获取内存警告就不足为奇了。

答案 3 :(得分:0)

release中的简单dealloc消息应该足够了。

你的第二个问题听起来像是一个过早释放的视图,但如果没有看到一些代码,我就不能说太多了。

答案 4 :(得分:0)

我在OS3中使用UIWebView遇到了类似的问题 - 这个描述是一个很好的起点,但是我发现在发布webView之前简单地忽略了Web视图委托解决了我的问题。

阅读示例代码(上面接受的答案) - 似乎有点矫枉过正。例如。 [webView发布]和webView = nil行完全相同,因为作者描述了变量的声明方式(所以你不需要两者)。我也不完全相信所有的保留和释放线 - 但我想你的里程会有所不同。

答案 5 :(得分:0)

  

可能相关,我有时(再次   得到一个丑陋的   heisenbug点击后面   某个其他视图的导航栏上的按钮   将弹出标题,但不是视图。   换句话说,我离开了   视图n的标题在堆栈上,但是   视图显示仍然是视图n + 1(   结果是你被困在这上面   屏幕,无法回到根   观点 - 你可以走另一个方向,   即推送更多视图并弹回   没有正确弹出的视图,   只是没有到根视图。唯一的   出路是退出应用程序)。在其他   次数相同的推动和   相同视图上的弹出窗口工作正常。

我有同样的问题,当我在堆栈中使用带有视图控制器的导航控制器时> 2,当前视图控制器索引> 2,如果在这个女性中发生记忆警告,它会引发同样的问题。

有一个解决方案,我在NavigationController中覆盖pop和push方法的许多实验后发现,包含视图控制器堆栈,带有堆叠ViewControllers的视图和超级视图等。

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface FixedNavigationController : 
UINavigationController <UINavigationControllerDelegate>{

}

@end

#import "FixedNavigationController.h"

static BOOL bugDetected = NO;

@implementation FixedNavigationController

- (void)viewDidLoad{
    [self setDelegate:self];
}

- (void)didReceiveMemoryWarning{
    // FIX navigationController & memory warning bug
    if([self.viewControllers count] > 2)
        bugDetected = YES;
}

- (void)navigationController:(UINavigationController *)navigationController 
didShowViewController:(UIViewController *)viewController 
animated:(BOOL)animated
{

    // FIX navigationController & memory warning bug
    if(bugDetected){
        bugDetected = NO;

        if(viewController == [self.viewControllers objectAtIndex:1]){
            [self popToRootViewControllerAnimated:NO];
            self.viewControllers = [self.viewControllers arrayByAddingObject:viewController];
        }
    }
}

@end

它适用于堆栈中的3个视图控制器。