EXC_BAD_ACCESS after calling popViewControllerAnimated - iphone

I'm getting an EXC_BAD_ACCESS after calling dismissModalViewControllerAnimated on my view controller. My project is based on the table view starter project, and RootViewController creates a view like this:
GobanVC *vc = [[GobanVC alloc] initWithNibName:#"GobanVC" bundle:[NSBundle mainBundle] coll:c];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
In GobanVC.m, I'm handling a button to dismiss the view:
- (IBAction) onDone:(id) sender;
{
[self.navigationController popViewControllerAnimated:YES];
}
For some reason the GobanVC object is getting over-released. I ran the allocation instrument, and I can see the reference count get set to 1 when I call alloc, then UIKit calls retain/release a bunch of times, and then my release above is handled. After that, none of the retain or releases are from my code, and after popViewControllerAnimated, the count becomes -1 eventually.
If I take the release above out, things seem to work okay, so it seems the count is off by exactly one somewhere.
Any ideas?

Maybe there is something wrong with GobanVC. Do you have the implementation for it?
Because if there is a retain for every release made by UIKit (there should be).
And you say that your calls are balanced as well (one alloc and one release).
Then it means that in the implementation of GobanVC there must be something wrong.

Let's count the retains:
Alloc makes it 1.
Release makes it 0.
So, what comes out of the stack when you call pop will have retain counter equal to 0, which is NOT what you want. If you either remove release or keep it and assign "vc" to an instance variable defined as "retain" property, you will be fine.
From the code you provided I can see the following:
Root controller creates an instance of GobanVC. The GobanVC's retain count is 1.
Root controllers pushes GobanVC instance to the navigation stack. I am not sure if push increases the retain count. Most probably yes. Then the retain count of GobanVC instance becomes 2.
You release the GobanVC instance, setting its counter to 1.
Your button handler resides in GobanVC (not in the Root controller). So, GobanVC pops ITSELF from the stack with retain count 0 (because if push increases the counter, pop will decrease it). This IS a problem.

Related

Keeping a class running in the background when another has been started

I will try and explain this as best as possible if I have this code here
ViewTwoController *home = [[ViewTwoController alloc] initWithNibName:#"contentscreen" bundle:nil];
[self presentModalViewController:home animated:YES];
[home release];
I will start a new .m and .h class. What I would like to try and do however is when this is called, have the .m and .h class where it was called from running in the background so I do not lose data.
The best example I can think of is with Android. If you begin a new class, and don't add the finish() statement in the class the call was made from, the previous class runs behind the current class (that was pushed to the front) and maintains all the data it originally had, so if you hit a return button, you will see the information you had moments ago. Is this possible? I can try add more detail if people cannot understand what I am trying to do.
You need to understand a objects life cycle a little better.
An object is brought into existence generally with a 2 part process.
allocation - (grabbing the memory for the object and its members)
initialization - (setting the object up for use)
This can be combined into single step with the +new class method which combines alloc and init.
lets introduce an example class called MyClass and an object of that class called myObject. By convention classes start with uppercase letters and objects start with lowercase letters. So Without further ado, some code:
MyClass * myObject;
this this makes an object pointer, but doesn't allocate any memory for it or direct the pointer to reference anything.
myObject = [[MyClass alloc] init];
this actually creates an instance of MyClass, passes the -init message to it, then assigns the return value of the init message to myObject. At this point the reference count of this object is 1.
myObject can potentially go out of scope but that alone doesn't free the memory that was allocated during the alloc step.
in order to free that memory a release message will need to be passed to the object.
[myObject release];
The effect of release is to decrement the reference count, if the reference count is already 1 then the object will then be passed the -dealloc indicating that it is actually being freed.
back to your question... essentially [self presentModalViewController:home animated:YES]; ends up calling -retain on home, so that it will not be destroyed until you dismiss the modal view controller. In affect when you call release or autorelease you aren't dealloc'ing the object, just telling the object:
"Hey, I don't need you anymore, and if no one else does either then free up all the memory that you grabbed earlier".
Your problem has nothing to do with "class running in the background" but more with how you manage your data.
When you present a modal view controller, its parent (the view controller you presented it from) isn't destroyed (unless you specifically release it, which would probably crash your app later). So if you're wondering whether its still in memory; it is. As for tasks still running, it depends on what those tasks are. For example, you can still send it messages (call methods) and it will gladly receive those messages from your or from a delegate and perform whatever action it has to while it's off-screen.
Hope that helped.
In this case you are presenting new view controller. The main thread will be in the new controller presented. If you want something to run in background in the previous view controller then you can create a background thread. This can be done using [self perfomselectorInThebackground ... ] Or some other methods like GCD. (The main thing is you should not block Main thread)

Iphone App crashes when releasing view controller containing scrolling UITableView

This is a very very... very odd bug. It's hard to describe the exact project I have, but I will try and create a simpler representation of the classes I have. It goes like this:
Assume I have a navigation controller as my top view controller. Inside it, at one moment in time I have a UIViewController, let's say a ContactsScreenController. The view for this contains multiple UITableView that each is controlled by a separate object of type MyTableController (delegate&datasource). I do this by keeping an array of controllers
// This is the interface for my screen controller. An object of this type goes in a top-
// level navigation controller
// MainScreenController.h
#interface ContactsScreenController : UIViewController
NSMutableArray* tableControllers;
#end
// MainScreenController.m
- (UITableViewCell*)cellForRowAtIndexPath..something..
{
// Here what I do is create a new controller if needed, and add it to tableControllers
// Memory allocations & releases are good because I checked with instruments
}
#define SAFE_DEL(x) { if (x != nil) { [x release]; x = nil; } }
- (void)dealloc
{
SAFE_DEL(tableControllers);
[super dealloc];
}
Now, MyTableController is a more complicated object as it handles fetching data from a web service, but basically what I do is I want to make sure that when the object is deleted, I cancel any pending data requests, like this:
// MyTableController.m
- (void)dealloc
{
[globalDataProvider cancelRequestsForController:self];
// release other objects i might have
[super dealloc];
}
OK, so this is my objects setup. The crash occurs when I am deleting the object tableControllers. It decrements the retainCount for my MyTableController objects and it reaches 0 (checked using Instruments). But for some UNKNOWN reason, I get calls for cancelRequestsForController, AFTER the retain count has been zero.
Obviously, I get a EXC_BAD_ACCESS.
Before you start thinking it's a problem with my retain/release pairs, the application runs perfectly if I am releasing the main screen controller while the inner tables are static. As soon as the are scrolling and I hit the Back button in the navigation controller I experience the bug.
I've checked using instruments the entire history of retain count changes for my inner controllers and it is good (no unusual stuff). When the bug occurs, my last entry in the history is from QuartzCore run_animation_callbacks with a retain count of -1.
Any ideas? :)
PS: As a quick solution to get the project going, I've moved the cancelRequestsForController in a separate method and I'm manually calling it for each object in tableControllers before the release. This way I am sure that there will be no calls after the release, no matter the state of the tableview.
- (void)dealloc
{
for (TableController* c in tableControllers)
[c cancelRequests];
SAFE_DEL(tableControllers);
[super dealloc];
}
But I don't like this solution for several reasons.
Thanks to everybody for the answers/comments.
The problem was generated by a call to [super dealloc] in an object, before I got the chance to release my objects. This caused a lot of crazy stuff to happen. I moved the [super dealloc] at the end of my dealloc method (after my releases) and it works fine now.
The SAFE_DEL macro is unnecessary and makes the code less readable. Simply do:
[someObject release], someObject = nil;
It won't matter if someObject is already nil and it makes the code more directly readable.
As soon as the are scrolling and I hit
the Back button in the navigation
controller I experience the bug.
Any time you have non-memory management logic, you have fragility. Namely, when dealloc is being executed, it is quite likely because there is an entire sub-graph of objects in your application that are being deallocated. Since deallocation order is largely non-deterministic, you can't safely trigger any complex behavior in dealloc without risk that you are going to message an already deallocated object.
The best solution would be to get that cancellation mechanism out of dealloc!
Two things to check:
1. does your globalDataProvider (or any other class for that matter) have a reference to your controller (i.e. to call it back with data?) Your retain counts could be zero but for the wrong reason. How are you allocating the array, as well as each controller in the array? Do you have #properties?
2. In your executables properties Arguements screen, set NSZombieEnabled=YES That will tell you exactly which object is being called when it has a zero retain count.
HTH,
-Mike

Tricky memory management in objective-c

After reading:
Memory management of a view controller in Objective-c
and
Does UIView's addSubview really retain the view?
I wrote the following code to toggle a subview:
#synthesize switchableView, viewSelector, currentSubview;
//...
if(switchableView.subviews.count != 0)
[[switchableView.subviews objectAtIndex:0] removeFromSuperview]]
self.currentSubview = (veiwSelector.selectedSegmentIndex == 0) ?
[ViewA new] : [ViewB new];
[switchableView addSubview:currentSubview.view];
//[currentSubview release]; //<---crashes if I uncomment this line
It seems to run fine if I comment out that release line, but I can't wrap my head around why. Here's the way I understand what happens and maybe someone can tell me where I go wrong:
So lets consider currentView:
A gets alloc-ed by the 'new' message--retain count=A:1
A gets retained by the setter--retain count=A:2
A's view gets (supposedly) retained--retain count=A:2.1
next time through...
A's subview gets released count=A:2
B gets alloc-ed by the 'new' message--retain count=B:1, A:2
A gets autoreleased by the setter-- B:1, A:1
B gets retained by the setter--B:1, A:1
nothing ever gets rid of A?
So should I change my code, or am I wrong about the way memory management works in this language...or both?-
Ok, step one, ignore the retainCount. It's one of those things Apple should rename to something like lsdjiofsudfoiwjeriowhfiuwhrteiuhweifhsdjkfhsiurwoieuriosfho so people won't guess it's name, and not list it in the documentation. For your purposes, it's entirely useless, so ignore it.
Now that I've said that, let's consider something: addSubview: DOES retain its argument, and removeFromSuperview releases the receiver.
Finally, it's hard to tell what currentSubview is. It has a view property which would lean towards a VC, however, the way you're using it by itself, would indicate its a normal view. Perhaps you can clarify so I can continue my answer.
Your understanding of retain and release is correct, as is your code. That suggests that the problem lies outside of the code you've posted. For example, you would have this problem if your currentSubView property was defined as assign instead of retain.
You code is not structured well, however. This would be much clearer:
self.currentSubView = [[ViewA new] autorelease];
Furthermore, view controllers are meant to be cached, not created and released each time the user toggles a display. Typically, you create your view controllers beforehand, and access their .view property when necessary to display the view. UIViewController will automatically deallocate non-visible views in low memory conditions, and re-allocate their view when the .view property is accessed.
Change the release line to
self.currentSubview = nil;
and I think you'll be fine. You're releasing, but not setting the property to nil. So, when it gets re-assigned next time through, release will be called again on it. But you already released it so... boom.

iPhone - Retain Count - Retain count goes up for no apparent reason

Quick question, hopefully I am just missing something simple. Ok I have one class that holds a pointer to another; MainMenuClass and NormalGameClass. Inside of the MainMenuClass I do the following.
m_NormalGame = [[NormalGameMode alloc] initWithNibName:#"NormalGameMode" bundle:[NSBundle mainBundle]];
m_NormalGame.delegate = self;
[self presentModalViewController:m_NormalGame animated:YES];
Now, I first noticed a problem whenever the NormalGameClass' dealloc function was not being called so I did some retainCount calls and for some reason once it makes its way back to the release function in MainMenu, its retain count is 6. Further digging has me very confused. The first line after viewDidLoad in NormalGameClass its [self retainCount] is 4. Anybody have any idea what could be going on here? I only call alloc on NormalGameClass once ever, and yet it is being retained up to 6? Strangely enough never past that. Thanks for any insight.
UPDATE: Was fiddling around with things and found this to be awkward.In the MainMenuClass, here is how I get rid of the NormalGame.
[self dismissModalViewControllerAnimated:NO];
m_NormalGame.delegate = nil;
[m_NormalGame release];
Now, with this setup, the dealloc for NormalGame is never called. However, if I call [m_NormalGame release] immediately after the one posted above, it calls the dealloc for NormalGame ...twice. =/ Paint me confused.
presentModalViewController retains the passed view controller, so you need to release the view controller passed if you do not autorelease it. In this case you would need to release m_NormalGame.
m_NormalGame = [[NormalGameMode alloc] initWithNibName:#"NormalGameMode" bundle:[NSBundle mainBundle]];
m_NormalGame.delegate = self;
[self presentModalViewController:m_NormalGame animated:YES];
**[m_NormalGame release];**
I would imagine that the -dismissModalViewControllerAnimated: call is not releasing the view controller until the dismissal is complete. You do need to balance your initial -alloc/-init of the controller with a -release, but you should not expect the -dealloc method to be called immediately. It may in fact be called during the next iteration of the run loop, if the object was autoreleased.
Are you saying that without two calls to release your dealloc is not called, or is it simply not called immediately?
Also, try not to inspect retain counts as that will only lead to confusion and headaches. Just follow the memory management rules properly.

Unloading ViewControllers from Apple's PageControl Example + UINavigationController's Relationship to its RootViewControllers

So I modified Apple's PageControl example to dynamically load various navigation controllers (along with their root view controllers) into the scroll view. I also added a technique that attempts to unload a navigation controller when it's no longer needed. I've only been at ObjC for a little over a month, so I'm not sure if I'm doing the unloading correctly. Please see my code below, followed by my questions.
First I create a mutable array and fill it with nulls, just like Apple does:
// Create dummy array for viewControllers array, fill it with nulls, and assign to viewControllers
NSMutableArray *array = [[NSMutableArray alloc] init];
for (unsigned i = 0; i <= kNumberOfPages; i++)
{
[array addObject:[NSNull null]];
}
self.viewControllers = array;
[array release];
...Later, I fill the array with UINavigationController objects like so (this is just partial code, please excuse the missing parts...the main idea is that I alloc a couple of things, assign them and then release):
id controller = [[classForViewController alloc] initWithNibName:NSStringFromClass(classForViewController) bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
[self.viewControllers replaceObjectAtIndex:page withObject:navController];
[navController release];
...Finally, if a page doesn't need to be loaded anymore I do this:
[self.viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
Questions:
My understanding is that once I replace the navigation controller in my viewControllers array with null, the array releases the navigation controller. Thus the navigation controller's retain count hits zero and it no longer takes up memory. Is this correct?
What about the root view controller inside the navigation controller? Do I need to do anything with it or does it get released automatically once the navigation controller's retain count hit zero?
Thanks!
Yes. Any object put into a collection is sent a retain message. Likewise any object removed from a collection is sent a release message, the cause of the removal is irrelevant.
Yes, all objects will release all the objects it owns when they are released.
This all boils down to the simple principle of ownership that Cocoa defines:
You own the object if you received it as return value by calling a method that:
Is named alloc or new.
Contains the word copy, such as copy and mutableCopy.
You own the object if you call retain.
You may only call release and autorelease on objects you own.
You must release all owned objects in your dealloc methods.
There is just one exception; delegates are never owned. This is to avoid circular references and the memory leaks they cause.
As a side effect this also means that when you yourself are implementing a method, you must return an auto released object unless you are implementing new, or a method with copy in it's name. Objects returned as out arguments are always autoreleased.
Follow this strictly and Objective-C can be treated as if it is garbage collected 95% of the time.
Presumably yes, once your retain count reaches zero (however or whenever that happens) your object will receive the dealloc message. You can put a breakpoint to ensure that is happening. Instruments comes with a Leaks utility that should help you find memory problems, it's a great tool and I suggest using it frequently.
I'm not quite sure what you mean by "do anything to it". I presume you mean release it. The general pattern is that if you alloc or retain, you release. You can roughly guess if there is going to be a problem if your allocs and retains outnumber your releases (or vice versa, you don't want to double release).