Is this causing EXC_BAD_ACCESS? - iphone

I'm getting a EXC_BAD _ACCESS after leaving the method below. At that point htmlDocument becomes invalid, which it should since it falls out of scope. But is that why I'm getting the error? By the time the contentView (UIWebView) loads, htmlDocument is gone. But doesn't contentView already have what it needs from loadHTMLString?
- (void)viewDidLoad {
[super viewDidLoad];
//something other processing here
NSString *htmlDocument = [NSString stringWithFormat:#"<html> \n"
"<body>%#</body> \n"
"</html>", aboutContent];
[contentView loadHTMLString:htmlDocument baseURL:[NSURL URLWithString:#"http://www.abc.com"]];
}
Is there a better way to follow this all the way to failure? Leaving this method is the end of the line for my code. SDK stuff from there.

From your second post, the line you have commented out ([button release]) releases an object already marked to be released automatically.
Either remove the "autorelease" where you are doing an alloc for the button or remove the [button release] statement for the code to compile without errors and exceptions.
If an object is marked for autorelease, calling a release on it will be the same as calling a release on a deallocated instance, hence the error.

At a glance I would say there is nothing wrong with this code, assuming 'aboutContent' is a valid pointer to an object.

You can try running your app with Guard Malloc on the simulator, but that's not guaranteed to turn up anything. I'd suggest you just start commenting out statements until you find the one that's causing the error.

It's not clear what's going on with the code snippet you've just provided without more context. That said, it looks like all you want to do is load some HTML locally in the device. Why not just do this?
- (void)viewDidLoad {
[webView loadRequest:[NSURLRequest
requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"help/about.html"
ofType:#"html"]isDirectory:NO]]];
}
Using this technique, you can author HTML-based documents right in your phone without the user being wise to the fact that they are actually looking at a web view: this includes Javascript, CSS, the whole ball of a wax. I've seen people actually have their iPhone app go out to the Internet just to render a static page on the Internet which really is not necessary.

From your sample code in the second post, the button you release UIBarButtonItem *button is not retained by anything following the release you have commented out - and so it is deallocated.
You need to add the button to another view (using adSubview) in order for it to display and then you can release it. The parent view will retain the button.
Of course if you are going to refer to the button again, your view controller should keep the retain and release the button in its dealloc.

Related

iOS init UIWebView to clear navigation history

I am setting uiWebviewSocial as desired on IB and is loading requests as normal. However, later I need also to init this uiwebview in order to clear goBack history. I am using another uiwebview object for this purpose, webViewBridge
- (void)viewDidLoad
{
[super viewDidLoad];
self.webviewBridge = [[UIWebView alloc] init];
self.webviewBridge = self.webViewSocial;
when I need to init UIWebViewSocial, I use following
self.webViewSocial = nil;
self.webViewSocial = [[UIWebView alloc] init];
self.webViewSocial = self.webviewBridge;
however, if I then make goBack action, uiwebview loads an old request when should have its history empty. What I am missing? thank you
Well, I've been going aroundUIWebView in Stack Overflow these days and is a mess, for me at least.
It seemsUIWebView cache works at its own, I mean we cannot change neither access some info that is exclusively managed by system...
About your problem, it seems although yourUIWebView is sent to nil, it keeps getting info from cache... so, you should clear cache firstable
You can check it here and there
The problem is that after you are init'ing the new webviewBridge or webviewSocial, you are immediately overwriting the new object with the assignment on the next line. Remove the third line in each method.
If I am not misunderstanding, when you load the view you get an "initial" webviewSocial (with history and contents??), then after some loadings you make in the UIWebView, these are stored as history in webviewBridge, isn't it?
When you send webviewSocial to nil, that shall be empty... but NOT webviewBridge... and then if you are reloading webviewSocial with webviewBridge (which in fact is previous whole history), that's why it happens... I think
Saludos

UIWebView EXC_BAD Access

Hy
I've got a UIView. In one method, I alloc a WebView, and set it's to a retain property.
self->webView= [[UIWebView alloc] initWithFrame:self.frame];
Then I start to load a HTML string. After it's loaded, I resize the view and start a callBack to the superview to resize. It's working.
My problem is that, if the user go back before the view has been loaded, the view's are released. Then my WebView throw a BAD_ACCESS.
Here is the dealloc method:
-(void)dealloc{
[self.webView setDelegate:nil];
[self.webView stopLoading];
[self setWebView:nil];
[htmlStr release];
[super dealloc];}
The callback trace is shown in the screenshot. The interesting is that, if I don't release the WebView it is work's like the charm. If I release the WebView, then when it's deallocated, I get an error message in the log:
![bool _WebTryThreadLock(bool), 0x4e05150: 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...][1]
EDIT: It has been fixed. It turns out, that my image loading method was guilty. I've start a new thread (NSOperationQueue and NSInvocationOperation in that) to load an image, and make a performSelectorOnMainThred: when it's finished. Whit that, I've loaded more than hundred small (1-2 KB) image, on every page switch, which was a really big overhead. When I wrote a method which download the images in one thread one by one, this bug has never came again
You are modifying UI from other tread than main. This is forbidden as UIKit is not thread-safe (hence the crash...). If you want to modify the UI from another thread, you must use:
performSelectorOnMainThread:withObject:waitUntilDone:
Other thing I've notice in your code is that you incorrectly release your properties in -dealloc. You should not use synthesized setters like this:
[self setWebView:nil]; // same as self.webView = nil;
You should not, because it can bring you lots of problems if you start using KVO (Key-Value Observing) on you properties... Instead just write:
[webView release];
or, if you want to avoid "The Heisenbug":
[webView release], webView = nil;
EDIT: you can also benefit from answer to this SO question: How to safely shut down a loading UIWebView in viewWillDisappear?
Dont call it with self.webView in dealloc, just use webView:
- (void)dealloc {
if ([webView isLoading])
[webView stopLoading];
[webView setDelegate:nil];
[webView release], webView = nil;
[super dealloc];
}
It has been fixed. It turns out, that my image loading method was guilty. I've start a new thread (NSOperationQueue and NSInvocationOperation in that) to load an image, and make a performSelectorOnMainThred: when it's finished.
Whit that, I've loaded more than hundred small (1-2 KB) image, on every page switch, which was a really big overhead. When I wrote a method which download the images in one thread one by one, this bug has never came again.

UIWebView acts differnetly in app store version than dev version

I've got a problem with an app that works perfectly in the simulator as well as a physical iPhone 4 and an iPhone 3GS. The app was approved and is now in the App Store, but the distribution build downloaded from the App Store exhibits a bug not seen in the dev/release build.
This is a free app, but is supported by local advertising. When the app launches (or returns from background), the AppDelegate attempts to download some HTML from our ad server, and if successful, presents a modal view controller with a UIWebView and passes an NSData variable containing the HTML. In development/release builds, this works PERFECTLY; the app launches, and after a few seconds, a view slides up and shows the ad, which can be dismissed with a button.
However distribution build from the App Store is different. When the modal view controller slides up, the UIWebView never loads. Remember, I present the view controller ONLY if able to download the ad data -- otherwise, the view is never presented.
Thankfully I implemented a timer in the ad view controller which will cause the modal view to dismiss itself if the webViewDidFinishLoad never fires (in which the timer is invalidated), so at least app users aren't too annoyed. But it's still ugly to have an empty view controller slide up and then slide away for apparently no reason.
Here's the relevant methods in the AppDelegate:
- (void)launchAd
{
[NetworkActivity showFor:#"ad"];
if (!alreadyActive && [ServerCheck serverReachable:#"openx.freewave-wifi.com" hideAlert:YES])
{
alreadyActive = YES;
[self performSelectorInBackground:#selector(downloadAdData) withObject:nil];
}
[NetworkActivity hideFor:#"ad"];
}
- (void)downloadAdData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *baseURL = #"http://appdata.freewave-wifi.com/ad/";
NSString *file = (IS_IPAD) ? #"ipad.php" : #"iphone.php";
NSURL *adURL = [NSURL URLWithString:[baseURL stringByAppendingString:file]];
adData = [[NSData alloc] initWithContentsOfURL:adURL];
[self performSelectorOnMainThread:#selector(presentAdModal) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)presentAdModal
{
if (adData)
{
AdViewController *adView = [[AdViewController alloc] initWithNibName:nil bundle:nil];
[adView setAdData:adData];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:adView];
[navController setModalPresentationStyle:UIModalPresentationFormSheet];
[navController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[tabBarController presentModalViewController:navController animated:YES];
[navController release], navController = nil;
[adView release], adView = nil;
}
else
LogError(#"Not presenting ad; unable to create data object.");
}
By the way, adData is defined in header with NSData *adData;
The AdViewController simply contains a UIWebView, which is loaded with
[webView loadData:adData MIMEType:#"text/html" textEncodingName:#"utf-8" baseURL:nil];
Again, this all works PERFECTLY, EVERY TIME with dev/release builds in simulator and physical devices -- just not on distribution build from app store. I have even converted the NSData to an NSString and barfed it out with NSLog() just to prove that the HTML was downloaded before presenting the AdView modally.
[sigh...]
EDIT 1: In case my original post was not clear, the webViewDidFinishLoad never gets called in distribution build (but it does in dev/release build).
EDIT 2: Also, just before I call
[webView loadData:adData MIMEType:#"text/html" textEncodingName:#"utf-8" baseURL:nil];
in the AdViewController, I added a temporary NSLog() and converted adData to NSString and logged it to the console, and the HTML was there. So, the UIWebView just refuses to load the NSData?
HOLY COW. I figure it out.
Okay, before I say what I found, I did want to correct my own original wording: the modal ad has never worked in the simulator, but always on devices. I know the simulator can have its quirks, so I never thought anything of it, especially since it always worked on the devices. I know this is an important detail that was missing for this discussion, but it's been a couple of weeks since I worked on this project, and I'd forgotten all about it until today.
Now then... While tinkering with things, I noticed the AdView.xib was not in my project file list. I expanded a few folders thinking maybe it was accidentally dragged into one of them, but it was not listed at all. This really has me puzzled, though -- Xcode NEVER complained about a missing resource (no warnings or errors; always a perfect compile).
So, I navigated to the physical location and added the AdView.xib into the project. Now, the modal ad is displayed in the simulator, which is a first. I figure that since now the app works correctly in the simulator, it should work fine in the distribution build (odd correlation to make, but it's all I got until my update hits the App Store).
Obviously, I'll be submitting an update, so I won't accept my own answer until after the update hits the App Store (assuming I have actually fixed it).
Ok, this is an extremely long shot, but perhaps worth considering.
The docs for NSData state that with regards to initWithContentsOfURL "The returned object might be different than the original receiver." So, if it was a different object, and one which was in fact autoreleased, consider this line in your code:
adData = [[NSData alloc] initWithContentsOfURL:adURL];
This won't add a retain count for adData -- you didn't write self.adData = or similar. So, bearing in mind the scenario mentioned whereby the returned NSData was autoreleased: your method downloadAdData wraps its content in an NSAutoreleasePool. This is correct practice. However, that might result in adData being released BEFORE presentAdModal is called on the main thread. So...
In presentAdModal you just check that adData isn't nil -- but it can be not nil, and still have been deallocated from memory at that point by your NSAutoreleasePool -- hence, you would in this situation trigger the "show web view" code, but be attempting to load an NSData object that had been trashed. Which probably would contain complete garbage, hence no successful "web view loaded" call.
As I said, a long shot, but the ony thing that jumps out at me at this point.
UPDATE:
A completely different cause of your problem might be this:
Your test environment (i.e. non App-Store builds) is making requests from a certain part of the internet (i.e. your office) which has permission to access the web server containing ads, due to either IP blocking or whatever network setup there is, whereas your App Store release builds are attempting to access the ad server from parts of the internet which are forbidden. Again, probably not the case, but worth mentioning.

UIWebView memory issues

I am playing youtube videos on a UIWebView which appears as a modalViewController subview (flip transition). Everything works fine, even though the UIWebView is released, I still receive memory warnings after a few repeated selection of this modalViewController.
I have added my UIWebView programmatically inside ViewDidLoad. Inside viewDidDisappear I check for [UIWebView retainCount] and if greater than 1, perform the following steps:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[self.webView removeFromSuperview];
self.webView.delegate = nil;
self.webView = nil;
NSLog(#"[self.webView retainCount] %d", [self.webView retainCount]);
I am running my code on xCode 3.2.5, iOS 4.2.
Appreciate all you help.
I think you are approaching the memory management problem in the wrong way. Checking the retainCount is a valid debugging technique if you know what you are doing. It is not, however, a memory management tool. In your particular case, if the UIWebView is being displayed it will always have retain count > 1. The superview will have a retain on it thus making the "if" useless.
If the webView property is well defined (i.e. noatomic, retain) the statement:
self.webView = nil;
should release the webView. A common mistake is to initialize the property with:
self.webView = [[UIWebView alloc] init];
This is likely to introduce a leak if the webView is defined as "retain". The correct way is
self.webView = [[[UIWebView alloc] init] autorelease];
If you can't display your controller several times without running out of memory you have a memory leak. Use Instruments (Leaks in particular) to find hte objects what are note being released properly. This is a good tutorial.
Be careful in keeping your retains and releases balanced and check for leaks.
Your problem will be related to this:
Is it possible to prevent an NSURLRequest from caching data or remove cached data following a request?
Scroll down to my answer for an extension of the accepted answer - i had this problem for days and it's now resolved!

Reused UIWebView showing previous loaded content for a brief second on iPhone

In one of my apps I reuse a webview. Each time the user enters a certain view on reload cached data to the webview using the method :-
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
and I wait for the callback call
- (void) webViewDidFinishLoad:(UIWebView *)webView.
In the mean time I hide the webview and show a 'loading' label.
Only when I receive webViewDidFinishLoad do I show the webview.
Many times what happens I see the previous data that was loaded to the webview for a brief second before the new data I loaded kicks in.
I already added a delay of 0.2 seconds before showing the webview but it didn't help.
Instead of solving this by adding more time to the delay does anyone know how to solve this issue or maybe clear old data from a webview without release and allocating it every time?
Thanks malaki1974, in my case I wasn't using a modal view.
When I sat with an Apple engineer on WWDC 2010 and asked him this question his answer was simply: "Don't reuse UIWebViews, that's not how they were ment to be used."
Since then I make sure to calls this set of lines before allocating a new UIWebView
[self.myWebView removeFromSuperview];
self.myWebView.delegate = nil;
[self.myWebView stopLoading];
[self.myWebView release];
That solved the issue.
Clear the contents of the webview before you try to load new content
[self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
First, the UIWebView renders it contents in a background thread. Even when you receive webViewDidFinishLoad: it might not be completely done. Specially if it is an ajax-intense page that comes from the network.
You say you are hiding the view. I wonder if that means that the webview delays its drawing completely. What you could try is to move the UIWebView offscreen or obscure it with another view. Maybe that will change it's drawing behaviour.
If you do not need an interactive UIWebView then you can also consider to do it completely offscreen in a separate UIWindow and then create an image from that UIWebView's layer.
That's what I do, and it works:
[_webView stringByEvaluatingJavaScriptFromString:#"document.open();document.close();"];
Try loading a local file that is blank or has a loading graphic when you hide it, rather than just loading new content when you show it. Since the file is local it will be quick and even if the new page takes a while to load it will have either blank or loading expected behavior.
If you got controll over the html. You can communicate back to objective-c when the document is ready. Like so in jQuery:
function messageNative (name, string) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "appscheme://" + name + "/" + string);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
$(function() {
messageNative('webview', 'ready');
});
And then in UIWebView's delegate method webView:shouldStartLoadWithRequest:navigationType: wait for the request with url equal to "appscheme://webview/ready". Then you should know: the document is loaded and ready for display. Then all that is missing is a simple fade-in or something like that :)