31

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

在我看来WillDisappear 处理程序我有这个

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

这似乎可以处理大多数情况,因为取消委托会阻止它将 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 视图的视图时才能触发它,并且在那之后它不会立即发生。

4

6 回答 6

53

对此的一个变体应该可以解决泄漏和僵尸问题:

- (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];
}
于 2009-05-05T23:09:51.487 回答
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 中释放整个容器。

于 2009-05-03T07:47:20.283 回答
1

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

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

于 2009-06-30T16:29:17.850 回答
0

一个简单的release消息dealloc应该就足够了。

您的第二个问题听起来像是一个过早解除分配的视图,但如果没有看到一些代码,我不能说太多。

于 2009-04-26T02:35:57.987 回答
0

我在 OS3 中使用 UIWebView 时遇到了类似的问题——这个描述是一个很好的起点,但是我发现在发布 webView 之前简单地取消 web 视图代理解决了我的问题。

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

于 2009-08-17T14:34:18.887 回答
-1

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

我有同样的问题,当我在堆栈> 2 和当前视图控制器索引> 2 中使用带有视图控制器的导航控制器时,如果此时发生 memoryWarning,它会引发同样的问题。

只有 1 个解决方案,经过多次实验,我在 NavigationController 中覆盖了 pop 和 push 方法、视图控制器堆栈、堆栈视图控制器的视图和超级视图等。

#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 个视图控制器。

于 2009-09-08T07:33:18.357 回答