How to safely shut down a loading UIWebView in viewWillDisappear?


up vote24down votefavorite

24

I have a view containing a UIWebView which is loading a google map (so lots of javascript etc). The problem I have is that if the user hits the 'back' button on the nav bar before the web view has finished loading, it is not clear to me how to tidily tell the web view to stop loading and then release it, without getting messages sent to the deallocated instance. I'm also not sure that a web view likes its container view disappearing before it's done (but I've no choice if the user hits the back button before it's loaded).

In my viewWillDisappear handler I have this

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

this seems to handle most cases OK, as nil'ing the delegate stops it sending the didFailLoadWithError to my view controller. However if I release the web view in my view's dealloc method, sometimes (intermittently) I will still get a message sent to the deallocated instance, which seems to be related to the javascript running in the actual page, e.g.:

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

If I simply don't release the webview, then I don't get these messages though I guess I'm then leaking the webview.

If I don't send the 'stopLoading' message, and simply release the webview within viewWillDisappear, then I see messages like this:

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

Possibly related, I sometimes (again totally intermittent) get an ugly heisenbug where clicking the back button on some other view's navbar will pop the title, but not the view. In other words I get left with the title of view n on the stack, but the view showing is still view n+1 (the result is you're trapped on this screen and cannot get back to the root view - you can go the other direction, i.e. push more views and pop back to the view that didn't pop corrrectly, just not to the root view. The only way out is to quit the app). At other times the same sequence of pushes and pops on the same views works fine.

This particular one is driving me nuts. I think it may be related to the view disappearing before the web view is loaded, i.e. in this case I suspect it may scribble on memory and confuse the view stack. Or, this could be completely unrelated and a bug somewhere else (i've never been able to reproduce it in debug build mode, it only happens with release build settings when I can't watch it with gdb :-). From my debug runs, I don't think I'm over-releasing anything. And I only seem to be able to trigger it if at some point I have hit the view that has the web view, and it doesn't happen immediately after that.

iphone cocoa-touch uiwebview


share|edit|flag

edited May 2 '09 at 9:56

Alex Reynolds
39.5k16108178

asked Apr 26 '09 at 0:15

frankodwyer
8,69453261

 

3  
   
If I were you, I'd file a bug with Apple. The web view should not be receiving any messages after it gets deallocated -- it ought to terminate JS processing and URL loading when it's dealloc'ed. I've seen the nav item issue occasionally, but only after I installed iPhone OS 3.0 beta. Maybe that's an OS bug, too? – Daniel Dickison May 5 '09 at 19:49
   
   
yes that's what I thought (dealloc should clean up). The apple docs aren't generally very clear on the contract with the objects they provide. I've never seen the nav thing before and it happens regularly enough in my program that I think it must be something I'm doing - hopefully related to this webview thing if I manage to fix it. – frankodwyer May 8 '09 at 12:28
   
   
oh and to add, this happens on 2.2.1 – frankodwyer May 8 '09 at 12:29
   
   
try setting the webView as associated object instead of ivar/property – myell0w Aug 4 '11 at 0:30

add comment (requires 50 reputation)

6 Answers

activeoldestvotes


up vote41down voteaccepted

+125

A variation on this should fix both the leaking and zombie issues:

-(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];}

share|edit|flag

edited Mar 12 '11 at 7:25

 

 

answered May 5 '09 at 23:09

rpetrich
23.2k44171

 

   
   
yes good answer, i was thinking of trying something like this. your answer has a few things i didn't think of though, so i'll try it and if it works I will accept this one. Unfortunately the bounty will expire before i get time to try this - but what I will do is reopen the bounty and accept the answer in that case (just to remind myself, it was 250 bounty). – frankodwyer May 8 '09 at 12:24
   
   
I wouldn't worry about the bounty too much. It's just a number (besides, half is awarded to the top answer anyway ;) – rpetrich May 8 '09 at 23:54
   
   
finally got around to trying this - looking pretty good so far. No funny messages and it even seems to have cured the heisenbug - it was pretty intermittent though so I won't cheer yet til I have tested some more. – frankodwyer Jun 12 '09 at 21:03
   
   
@rpetrich: I know it's past 2nd anniversary of your answer, but why the fix is using retain/release onself? Wouldn't that cause problems for didReceiveMemoryNotification or forced call ofviewDidUnload? Could you please put some more light on why retain/release is used for a view controller which (I assume) has UIWebView outlet as property? – matm Jun 8 '11 at 9:12
   
   
@rpetrich: I think calling [self release] for [webView isLoading] == YES in viewDidUnloadshould balance retain count if e.g. webView has a long timeout, connection is slow and view is forced to unload. Am I correct? – matm Jun 8 '11 at 9:19

show 6 more comments


up vote1down vote

The UINavigationController bug you're describing in the second part of your post might be related to your handling of memory warnings. I've experienced this phenomenon and I"ve been able to reproduce it on view n in the stack by simulating a memory warning while viewing view (n+1) in the stack.

UIWebView is a memory eater, so getting memory warnings wouldn't be surprising when it's used as part of a view hierarchy.


share|edit|flag

answered Jun 30 '09 at 16:29

 

Armaggi

 

   
   
That's interesting, thanks. I will read up on what I'm meant to do in response to a memory warning (right now I don't handle it at all). I have not actually seen this issue since I moved to 3.0 and dumped the webview in favor of mapkit, and generally tightened up on other areas of memory handling. – frankodwyerJun 30 '09 at 16:50

add comment (requires 50 reputation)


up vote0down vote

A simple release message in dealloc ought to be enough.

Your second problem sounds like a prematurely deallocated view, but I can't say much without seeing some code.


share|edit|flag

answered Apr 26 '09 at 2:35

Can Berk Güder
32.8k1176109

 

   
   
yes a simple release in dealloc is what I had, and this is what results in the message getting sent to the deallocated instance. The webview is declared with @property (nonatomic,retain) if that makes a difference. Also, I have NSZombie checking on and otherwise I don't see any messages going to deallocated views - also as I say I can't reproduce this at all in Debug mode, it only happens in release mode. – frankodwyerApr 26 '09 at 9:36

add comment (requires 50 reputation)


up vote0down vote

There's a few ways to handle it, but this should work. You want the didFailLoadWithError message, it's what tells you it's stopped.

Set a flag isLeaving=YES; Send the Webview a stopLoading.

In didFailLoadWithError:, check for the error you get when the webview stops:

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

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

}

release the webView in shootWebview:

 



 

variations: if you want to be cavalier about it, you can do the performSelector:withObject:withDelay: with a delay of [fillintheblank], call it 10-30 seconds without the check and you'll almost certainly get away with it, though I don't recommend it.

You can have the didFailLoadWithError set a flag and clean it up somewhere else.

or my favorite, maybe you don't need to dealloc it all when you leave. Won't you ever display that view container again? why not keep it around reuse it?

Your debug being different from release issue, you might want to check your configuration to make sure that it's exactly the same. Bounty was on the reproducible part of the question, right? ;-).

-- Oh wait a second, you might be taking a whole View container down with the WebView. You can do a variation on the above and wait to release the whole container in shootWebView.


share|edit|flag

answered May 3 '09 at 7:47

dieselmcfadden
1732

 

   
   
my problem is that once I get the viewWillDisappear message, my view controller is going away and will shortly be dealloced (along with everything it contains, i.e. the webview). I don't see any way to prevent the view controller being dealloced (without leaking it) as it's not me that does that. Hence any delegate messages will (sometimes) wind up going to the dealloced instance of my controller. – frankodwyer May 3 '09 at 11:54
   
   
This sounds wonky even as I type it, but I suppose you could [self retain] before your webview loading calls and balance by [self release] in the didfinishload and didfailloads to make sure everything sticks around until the WebViews complete properly. – dieselmcfadden May 3 '09 at 23:49
   
   
yes I was thinking along those lines myself, and like you I thought it was a bit wonky - don't see why it shouldn't work tho. Will try this, along the lines of rpetrich's answer. – frankodwyer May 8 '09 at 12:23

add comment (requires 50 reputation)


up vote0down vote

Possibly related, I sometimes (again totally intermittent) get an ugly heisenbug where clicking the back button on some other view's navbar will pop the title, but not the view. In other words I get left with the title of view n on the stack, but the view showing is still view n+1 (the result is you're trapped on this screen and cannot get back to the root view - you can go the other direction, i.e. push more views and pop back to the view that didn't pop corrrectly, just not to the root view. The only way out is to quit the app). At other times the same sequence of pushes and pops on the same views works fine.

I have the same problem, when I'm use navigation controller with view controllers in stack > 2 and current view controller index > 2, if an memoryWarning occurs in this momens, it raises the same problems.

There is inly 1 solution, which I found after many experiments with overriding pop and push methods in NavigationController, with the stack of view controllers, with views and superviews for stacked ViewControllers, etc.

#import <UIKit/UIKit.h>#import <Foundation/Foundation.h>@interfaceFixedNavigationController:UINavigationController<UINavigationControllerDelegate>{}@end

 



 

#import "FixedNavigationController.h"static BOOL bugDetected = NO;@implementationFixedNavigationController-(void)viewDidLoad{[self setDelegate:self];}-(void)didReceiveMemoryWarning{// FIX navigationController & memory warning bugif([self.viewControllers count]>2)
    	bugDetected = YES;}-(void)navigationController:(UINavigationController*)navigationController
didShowViewController:(UIViewController*)viewController
animated:(BOOL)animated
{// FIX navigationController & memory warning bugif(bugDetected){
    	bugDetected = NO;if(viewController ==[self.viewControllers objectAtIndex:1]){[self popToRootViewControllerAnimated:NO];
    		self.viewControllers =[self.viewControllers arrayByAddingObject:viewController];}}}@end

It works fine for 3 view controllers in stack.


share|edit|flag

answered Sep 8 '09 at 7:33

abuharsky
358414

  add comment (requires 50 reputation)

up vote0down vote

I had a similar problem to this using a UIWebView in OS3 - this description was a good starting point, however I found than simply nil'ing out the web view delegate before releasing the webView solved my problem.

Reading the sample code (the accepted answer - above) - it seems like a lot of overkill. E.g. [webView release] and webView = nil lines do exactly the same thing given the way the author describes the variable is declared (so you don't need both). I'm also not fully convinced by all the retain and release lines either - but I guess your mileage will vary.


share|edit|flag

answered Aug 17 '09 at 14:34

TimM
1931211

  add comment (requires 50 reputation)

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 330987132 | Go:217696290 | Python:336880185 | 做人要厚道,转载请注明出处!http://www.cnblogs.com/sunshine-anycall/p/3314691.html

时间: 2024-08-30 17:54:11

How to safely shut down a loading UIWebView in viewWillDisappear?的相关文章

ehcache历史变迁及常用API的使用(转)

  ehcache是一个用Java实现的使用简单,高速,实现线程安全的缓存管理类库,ehcache提供了用内存,磁盘文件存储,以及分布式存储方式等多种灵活的cache管理方案.同时ehcache作为开放源代码项目,采用限制比较宽松的Apache License V2.0作为授权方式,被广泛地用于Hibernate, Spring,Cocoon等其他开源系统. Ehcache的类层次模型主要为三层,最上层的是CacheManager,他是操作Ehcache的入口.我们可以通过CacheManage

IOS中UIWebView加载Loading的实现方法

  最近有朋友问我类似微信语音播放的喇叭动画和界面图片加载loading界面是怎样实现的,是不是就是一个gif图片呢!我的回答当然是否定了,当然不排除也有人用gif图片啊! 第一种方法:使用UIView and UIActivityIndicatorView 代码如下: //创建UIWebView WebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 44, 320, 400)]; [WebView setUserInteraction

iOS中UIWebView网页加载组件的基础及使用技巧实例_IOS

基本用法示例 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. UIWebView * webView = [[UIWebView alloc]initWithFrame:CGRectMake(0, 20, ScreenWidth, ScreenHeight-20)]; // 自动队页面进行缩放以适应屏幕 webView.scalesPageToFit = Y

iOS中UIWebView的使用详解

iOS中UIWebView的使用详解 一.初始化与三种加载方式      UIWebView继承与UIView,因此,其初始化方法和一般的view一样,通过alloc和init进行初始化,其加载数据的方式有三种: 第一种: - (void)loadRequest:(NSURLRequest *)request; 这是加载网页最常用的一种方式,通过一个网页URL来进行加载,这个URL可以是远程的也可以是本地的,例如我加载百度的主页: ? 1 2 3     UIWebView * view = [

UIWebView

UIWebView 简介 UIWebView主要用来加载网页内容到你的APP.要想实现这些,你需要创建一个UIWebView对象,然后把它放到Window(或者其他View)上,发送一个请求网页内容的请求即可.你也可以使用这个类去实现查看网页的前进和后退,你甚至可以动态地去改变请求网页的内容. 方法(不是所有) -(void)reload 该方法的作用是重新加载当前的页面,可用于网页的刷新. -(void)stopLoading 该方法用于停止加载网页.比如当网页在加载时网速太慢会一直提示加载中

iOS中 UIWebView加载网络数据 技术分享

版权声明:本文为博主原创文章,未经博主允许不得转载. 直奔核心: [objc] view plain copy #import "TechnologyDetailViewController.h"   #define kScreenWidth [UIScreen mainScreen].bounds.size.width   #define kScreenHeight [UIScreen mainScreen].bounds.size.height   @interface Techn

IOS之UIWebView的使用(基本知识)_IOS

刚接触IOS开发1年多,现在对于混合式移动端开发越来越流行,因为开发成本上.速度上都比传统的APP开发要好,混合式开发是传统模式与PC网页端相结合的模式.那么提到了 APP的混合模式开发,在Android开发中有WebView作为混合模式开发的桥梁,当然在IOS中也同样有一个 UIWebView 组件来作为混合模式开发的桥梁,那么下面就对UIWebView的一些基本知识详解一下. 一.UIWebView的基础使用 1.创建UIWebView: CGRect bouds = [[UIScreen

IOS中UIWebView的使用详解_IOS

一.初始化与三种加载方式 UIWebView继承与UIView,因此,其初始化方法和一般的view一样,通过alloc和init进行初始化,其加载数据的方式有三种: 第一种: - (void)loadRequest:(NSURLRequest *)request; 这是加载网页最常用的一种方式,通过一个网页URL来进行加载,这个URL可以是远程的也可以是本地的,例如我加载百度的主页: UIWebView * view = [[UIWebView alloc]initWithFrame:self.

在iOS应用中使用UIWebView创建简单的网页浏览器界面_IOS

UIWebView是iOS sdk中一个最常用的控件.是内置的浏览器控件,我们可以用它来浏览网页.打开文档等等.这篇文章我将使用这个控件,做一个简易的浏览器.如下图: 我们创建一个Window-based Application程序命名为:UIWebViewDemo UIWebView的loadRequest可以用来加载一个url地址,它需要一个NSURLRequest参数.我们定义一个方法用来加载url.在UIWebViewDemoViewController中定义下面方法: 复制代码 代码如