I have a problem I can't locate clearly, maybe you can help me...
I have an iPad project, based on UINavigationController, most (but not all) of controllers inside are instances of UITableViewController, and everything works well...
Everything excepting that my application crashes randomly, sometimes after 10 minutes of use, sometimes after only 10 seconds...
It never crashes on the same view, never at the same time, making that difficult to reproduce.
In addition, it only seams to happen on device, I've never got this crash in the simulator.
The debugger doesn't help me very much, here is what it says:
-[UITableView autorelease]: message sent to deallocated instance 0x8e9800
And here is the call stack:
http://i.stack.imgur.com/JSCHx.png
Any idea ?
Thanks (and sorry for my english)
You're overreleasing a UITableView somewhere in your code. Are you calling release or autorelease on the UITableView inside one of your UITableViewControllers? You should only release objects that you 'own'. You get to own an object by using methods beginning with alloc, new, copy, or retain.
Please read the cocoa memory management guidelines for more info.
Useful links:
http://www.cocoadev.com/index.pl?MemoryManagement
http://www.cocoadev.com/index.pl?RulesOfThumb
At some point you are either releasing a UITableView instance that you do not own or you are failing to retain one at some point where you keep a reference to it (e.g. you store it in an ivar or a property declared assign rather than retain).
I have written about how to debug things like this on my blog:
http://loufranco.com/blog/files/Understanding-EXC_BAD_ACCESS.html
Basically, try these three things first:
Do a Build an Analyze and fix everything you see
Turn on Zombies, run your code -- it will tell you if you talk to dealloced objects
If that fails, try Debug Malloc, but that's way harder.
I apologize, after re-reading all my source code, I found ONE ViewController (I have around 20 ViewController), where I released an Outlet, in ViewDidUnload.
The reason that it crashed randomly is that I didn't understood well the mechanism of ViewDidUnload, it is called to release views (but not objects of controllers) when memory is low and view is not visible (ex: First ViewController of a NavigationController), and the views are re-loaded when the ViewController become visible again...
In simulator, memory is rarely an issue so ViewDidUnload is almost never called...
Problem fixed, thank you everyone for your help
To help with making sense of the trace, see iOS Debugging Magic (Technical Note TN2239) and Understanding and Analyzing iPhone OS Application Crash Reports (Technical Note TN2151).
Jeff
Related
I made a universal application that contains NIB files for both ipad and iphone UI's. In my view controllers initWithNibName method I call UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM() to detect whether the controller is running on iphone or ipad.
I then launch their respective nib files. When I run the app on iphone, it works fine, but when I run it on ipad it eventually crashes with a EXC_BAD_ACCESS error. This error occurs when I use a view controller to launch another view controller, which then launches another one in the navigation stack. This error occurs as soon as I click the view that belongs to the third controller of the stack.
I cannot distinguish a difference between the NIB files that would cause a crash. I have been working tirelessly to figure out why this is happening but I cannot fix this error. Does anyone have any insight into what might be going on?
Any advice on how to approach fixing this problem would be very appreciated.
The first thing you should do is enable the "All Exceptions" break point. This will often accurately tell you the line of code where the EXC_BAD_ACCESS is happening.
Next, I would turn on zombies and see where the over-release is happening. To do so, in Xcode, while holding the option key, click Product | Run.... In the ensuing window, add NSZombieEnabled to the environment variables list.
Then run. Do the normal things you do to cause the crash and see where the debugger lands. With any luck, it will stop where the problem is actually occurring.
When you get a EXC_BAD_ACCESS it means you're trying to access/release something that's already been released. If you're in a non-ARC situation, it normally means you've inadvertently released something when you didn't mean to, so just check for alloc/init and release balance. If, however, you're in an ARC situation, I would bet it has to do with not niling a delegate when a view controller gets released.
For example, if you have a MKMapView and have set its delegate to your view controller, you should nil its delegate when your view gets unloaded or dealloc'd. Otherwise, messages will continue to get set to it. Or, another possibility is that you've added your view controller as an NSNotificationCenter observer and didn't remove it as an observer when the view controller was unloaded or dealloc'd.
Another possibility is that you're re-using view controllers between the two versions of your universal app. If you are accessing anything by an identifier that doesn't exist in the nib for the iPad, that would cause a crash--though if you're using nibs as opposed to storyboards, that may not be an issue.
That's about all I can think of for now. Try to zero in on where it's happening and post code here if you still can't figure it out.
Best regards.
I converted my app to ARC and noticed that an object alloc'ed in one of my view controllers was not being dealloc'ed when that view controller was dealloc'ed. It took a while to figure out why. I have Enable Zombie Objects on for my project while debugging and this turned out to be the cause. Consider the following app logic:
1) Users invokes action in RootViewController that causes a SecondaryViewController to be created and presented via presentModalViewController:animated.
2) SecondaryViewController contains an ActionsController that is an NSObject subclass.
3) ActionsController observes a notification via NSNotificationCenter when it is initialized and stops observing when it is dealloc'ed.
4) User dismisses SecondaryViewController to return to RootViewController.
With Enable Zombie Objects turned off, the above works fine, all objects are deallocated. With Enable Zombie Objects on ActionsController is not deallocated even though SecondaryViewController is deallocated.
This caused problems in my app b/c NSNotificationCenter continues to send notifications to ActionsController and the resulting handlers cause the app to crash.
I created a simple app illustrating this at https://github.com/xjones/XJARCTestApp. Look at the console log with Enable Zombie Objects on/off to verify this.
QUESTION(S)
Is this correct behavior of Enable Zombie Objects?
How should I implement this type of logic to eliminate the issue. I would like to continue using Enable Zombie Objects.
EDIT #1: per Kevin's suggestion I've submitted this to Apple and openradar at http://openradar.appspot.com/10537635.
EDIT #2: clarification on a good answer
First, I'm an experienced iOS developer and I fully understand ARC, zombie objects, etc. If I'm missing something, of course, I appreciate any illumination.
Second, it is true that a workaround for this specific crash is to remove actionsController as an observer when secondaryViewController is deallocated. I have also found that if I explicitly set actionsController = nil when secondaryViewController is dealloc'ed it will be dealloc'ed. Both of these are not great workaround b/c they effectively require you to use ARC but code as if you are not using ARC (e.g. nil iVars explicitly in dealloc). A specific solution also doesn't help identify when this would be an issue in other controllers so developers know deterministically when/how to workaround this issue.
A good answer would explain how to deterministically know that you need to do something special wrt an object when using ARC + NSZombieEnabled so it would solve this specific example and also apply generally to a project as a whole w/o leaving the potential for other similar problems.
It is entirely possible that a good answer doesn't exist as this may be a bug in XCode.
thanks all!
Turns out, I've written some serious nonsense
If zombies worked like I originally wrote, turning on zombies would directly lead to innumerable false positives...
There is some isa-swizzling going on, probably in _objc_rootRelease, so any override of dealloc should still be called with zombies enabled. The only thing that won't happen with zombies is the actual call to object_dispose — at least not by default.
What's funny is that, if you do a little logging, you will actually see that even with ARC enabled, your implementation of dealloc will call through to it's superclass's implementation.
I was actually assuming to not see this at all: since ARC generates these funky .cxx_destruct methods to dispose of any __strong ivars of a class, I was expecting to see this method call dealloc — if it's implemented.
Apparently, setting NSZombieEnabled to YES causes .cxx_destruct to not be called at all — at least that's what happened when I've edited your sample project:
zombies off leads to backtrace and both deallocs, while zombies on yields no backtrace and only one dealloc.
If you're interested, the additional logging is contained in a fork of the sample project — works by just running: there are two shared schemes for zombies on/off.
Original (nonsensical) answer:
This is not a bug, but a feature.
And it has nothing to do with ARC.
NSZombieEnabled basically swizzles dealloc for an implementation which, in turn, isa-swizzles that object's type to _NSZombie — a dummy class that blows up, as soon as you send any message to it. This is expected behavior and — if I'm not entirely mistaken — documented.
This is a bug that has been acknowledged by Apple in Technical Q&A QA1758.
You can workaround on iOS 5 and OS X 10.7 by compiling this code into your app:
#import <objc/runtime.h>
#implementation NSObject (ARCZombie)
+ (void) load
{
const char *NSZombieEnabled = getenv("NSZombieEnabled");
if (NSZombieEnabled && tolower(NSZombieEnabled[0]) == 'y')
{
Method dealloc = class_getInstanceMethod(self, #selector(dealloc));
Method arczombie_dealloc = class_getInstanceMethod(self, #selector(arczombie_dealloc));
method_exchangeImplementations(dealloc, arczombie_dealloc);
}
}
- (void) arczombie_dealloc
{
Class aliveClass = object_getClass(self);
[self arczombie_dealloc];
Class zombieClass = object_getClass(self);
object_setClass(self, aliveClass);
objc_destructInstance(self);
object_setClass(self, zombieClass);
}
#end
You will find more information about this workaround in my blog post Debugging with ARC and Zombies enabled.
Turns out it is an iOS bug. Apple has contacted me and indicated they've fixed this in iOS 6.
to answer the second question you would need to remove the observer from NSNotification - that will keep it from calling the view.
Normally, you would do this in the dealloc but with that zombie issue maybe it's not getting called. Maybe you could put that logic in viewDidUnload?
Because you have open NSZombieEnabled, this let the object not call dealloc, and put the object to a special place. you can close NSZombieEnabled and have try again. And double check if your code have circle retain condition.
I've got a fairly basic app that I'm updating - and as part of the update I'm implementing memory warning management.
When the memory warning method gets called, I release any view controllers not in use (and these in turn release any of their objects). Everything seems to work fine, there are no leaks etc as far as I can tell.
What isn't working:
Using the 'allocations' instrument, there is a lot of memory that isn't being released when a simulated hardware warning is called. Here's what I do when testing:
1 - start the app - this is the original jump in memory shown below.
2 - add a new view controller - this is the second spike.
3 - Return to the main view controller and simulate a hardware memory warning - this is the (small) drop in memory towards the end. This warning should completely release the additional view controller and associated objects.
Although everything is released, there is a lot of memory that remains. As far as I can tell, it's something like cached animations etc that iOS does. In a real low memory situation though, this should be released, and not stick around as this is where the majority of the memory goes.
How can this memory be released - or what am I doing wrong? Any pointers would be much appreciated - thanks!
-
EDIT: Thanks for all the answers so far! Although unfortunately I still haven't been able to solve the issue yet. Furthermore, the memory weirdness only seems to occur when using modal view controllers.
I've noticed that I actually have a background loading method for the extra view controller called when the app launches, to make things run smoothly. This indicates that the second spike in memory is completely due to something other that the view controller - maybe animations or something? Anyhow the problem still remains - what is this extra memory being used for, and how do I release it when I need to?
I could potentially create a mini project that exhibits the behavior if it would help. Thanks :)
Are you really sure you are not retaining your UIViewController or its objects? You shouldn't have to simulate a memory warning to see a decrease in allocations after releasing your controllers.
I attached a screenshot showing how my application looks after pushing and popping a UIViewController three times, using a UINavigationController.
EDIT
To answer your comments: UIViewController belongs to UIKit and is not thread safe, which means you should not be creating one in a background thread. This could potentially be the cause of the memory leak, since it will not be added to the main autorelease pool.
There's a really-really great screencast about memory analysis with Instruments from WWDC 2010. That's how I learned how to track similar issues.
You should check out http://developer.apple.com/videos/wwdc/2010/. To be specific http://developer.apple.com/videos/wwdc/2010/?id=311.
I'm writing a navigation app for iPhone at the moment and I'm having a very weird crash issue and was wondering if anyone had come across (and solved) this issue.
I have two views, both of which contain UITableViews and one that uses cells loaded from a nib. When I push and pop from one view to the other, after a couple of presses (usually 7 to 10) with everything loading and displaying as it should the app suddenly crashes. The debugger shows that CALayer was the last thing running, but I don't use any custom implementation of this class.
My first thought is that I've over-released an object, but after two days of playing with the code I can't identify any zombies.
Does anyone know what's going on here? Can post parts of code if required.
UPDATE:
Looks like zombies are being created on UIView delegate methods, namely viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear. Will investigate further tomorrow. :D
What you can do is to set breakpoints at the dealloc methods of the related classes, and see if the crash happens in one of the method. And also usually by looking at the callstack when the crash is happening, you can tell whether it's a memory related crash or not.
Apple Developer Reference Library has a class reference for WebPreferences
I've searched SO, Dev Forums and Googled without any relevant results.
EXC_BAD_ACCESS signal is generated.
I can't find a crash report.. its happening on the simulator. The debugger is called, and I don't get a crash report.
EDIT
This is triggered when tapping a UITextField, leaving a UITextField or if a UITextField is set as first responder when loading a view (push on by a navigation controller).
It is not easy to reproduce. I can go for a hundred app-launch/debug cycles before it will happen again. And then it might happen 3 times in 5 launches.
I do have a thread list in the debugger that shows several references to WebPreferences.
You're on the right track if you are using NSZombie. EXEC_BAD_ACCESS is caused by accessing released objects.
It is normal for EXEC_BAD_ACCESS to "crash" in code paths that do not belong to you. Most likely your code created the over-released object.
The key part of using NSZombie is running the malloc_history on the command line. You'll get the call stack showing where the over-released object originated from. For example:
alt text http://static.benford.name/malloc_history.png
The screenshot shows my app crashing at [NSString stringByTrimmingCharactersInSet:] but that is certainly not who caused the crash.
I technique I use is to look at the earliest code path that you own. Most of the time the mistake lies there.
In this case, the object originated from the class [JTServiceHttpRequest requestFinished], in which I was not properly retaining a object.
If all else fails, go through all your code paths listed and verify your use of proper memory management rules.
My bet is that WebPreferences and UITextField behavior have nothing to do with the crash.
For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.
This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.
It especially helps in background threads when the Debugger sometimes craps out on any useful information.
VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
NSLog(#"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.
The fact that it's crashing in _integerValueForKey: makes me strongly suspect that it's crashing on an over-released NSNumber. Over-releasing an NSNumber can lead to such bizarre crashes that it's almost humorous. Here's what happens:
You create a NSNumber for the integer "2"
The NSNumber class caches the NSNumber
You over-release it
The NSNumber class's cache now points to bad memory
Some completely unrelated piece of code creates an NSNumber for the integer "2"
The NSNumber class looks it up in its cache, and....
Bang
If you're running Snow Leopard, hit Cmd-Shift-A to let the analyzer look for memory management problems. Otherwise, go hunting in your use of NSNumbers.
agreed with previous responders about NSZombie. Most often this thing happens when you use your class as a delegate of a UITextView(or any else class) and also refer it in IBOutlet variable. when you leave your viewcontroller - it become deallocated. but if you didn't release IBOutlet variable in - (void) dealloc method - UITextView will still send calls to released delegate (your view controller).
Sounds more like an Auto-Release bug. You might be releasing something you don't "own" and the NSAutoRelease pool is running and trying to clean up something that was already released?
Did you release something you didn't "alloc"? For example, you wouldn't do:
NSString *test = #"testing";
[test release];
As this will cause the crash to happen when the Auto Release pool runs and attempts to release the NSString.