First of all, i have never seen so many memory issues in my app since i started placing "self" everywhere after reading an article about how memory behaves in obj-C. Now, im getting all kinds of issues (reveals the sloppiness of my coding). Granted I am a newbie at Objective-C, i'll admit i have never had so much issues with memory management before in my life. But i reckon it takes practice to get used to this.
Now, on to my question.
I have a class interface property (self.todoCreate) that holds a reference to the above controller. This controller is navigated to by pressing a button.
#property (nonatomic, retain) TodoTaskCreateController *todoCreate;
The code below are the snippets that cause the navigation view change:
TodoTaskCreateController *viewController = [[TodoTaskCreateController alloc]
initWithNibName:#"TodoTaskCreateController"
bundle:[NSBundle mainBundle]];
self.todoCreate = viewController;
[viewController release];
// slide-in todoCreate controller.
if (self.navigationController != nil && self.todoCreate != nil) {
[self.navigationController pushViewController:self.todoCreate animated:YES];
}
So here is my problem:
The first time i run this it works.
Once I'm on the second view screen, I navigate back to the main view.
And if i try to navigate again a 2nd time, then the app crashes, right where self.todoCreate is being assigned viewController.
Note that within the main view's viewDidAppear method, I call [self.todoCreate release].
Can anyone explain this?
PS - No wonder so many iPhone apps randomly crash.
todoCreate is a property, which means when you assign a value to it, it invokes a method called setTodoCreate which looks something like:
- (void) setTodoCreate:(Foo*) newVal
{
[todoCreate release]; // release the previous object
todoCreate = [newVal retain]; // point to new object, and also retain it
}
Now your viewDidAppear method is releasing self.todoCreate at which point the retain count of todoCreate is 0. When you create a new TodoTaskCreateController and assign it to self.todoCreate another release is performed, but this time on an object with retain count of 0.
Instead of [self.todoCreate release], you should be using self.todoCreate = nil.
You probably shouldn't be continually destroying and creating your TodoTaskCreateController either.
Your comment regarding application crashing is most likely due to developers not testing their application to see if it handls memory warnings properly. In the simulator there is a menu option to simulate this, and your application should "survive" the warning in all of its views.
I'm not going to speak to soon, but it APPEARS that i have resolved the crash by simply adding the viewController to the autorelease pool, and then removing all manual occurances of its release.
Now does it matter if my #property for createTodo is defined as (nonatomic, retain) as opposed to (nonatomic, assign)?
Related
I recently converted my AudioUnit plugin to take advantage of ARC for all the interface code (Cocoa). However, this resulted in the main NSView (the one created by the CocoaViewFactory and returned to the plugin as a property) never having -dealloc called. This made it impossible to dispose of the AUEventListener constructed for the NSView - which is documented to cause crashes - and creates a tonne of memory leaks (none of the Objective-C objects retained by the NSView are deallocated, either).
I 'solved' the problem by compiling only the CocoaViewFactory.m file without ARC, and returning the NSView at the end of -(NSView*)uiViewForAudioUnit with an autorelease. NSView -dealloc is now called when the window is closed, and memory is cleaned up as expected.
I'm a little unsure of myself with memory management, so am worried that this solution is going to cause some other memory bug. I can't find any documents that use ARC for the Cocoa code as a basis to work from. Does anyone have any ideas or experience with this?
EDIT:
The non-ARC version of the CocoaViewFactory.m file is :
- (NSView *)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize {
if (! [NSBundle loadNibNamed: #"PluginName_CocoaView" owner:self]) {
NSLog (#"Unable to load nib for view.");
return nil;
}
[uiFreshlyLoadedView setAU:inAU];
NSView *returnView = uiFreshlyLoadedView;
uiFreshlyLoadedView = nil; // zero out pointer. This is a view factory. Once a view's been created
// and handed off, the factory keeps no record of it.
return [returnView autorelease];
}
The uiFreshlyLoadedView variable is declared in CocoaViewFactory.h like so:
#interface PluginName_CocoaViewFactory : NSObject <AUCocoaUIBase>
{
IBOutlet PlugingName_CocoaView * uiFreshlyLoadedView;
}
It is not a property, it is bound directly to the NSView in the XIB file.
The only change I made when attempting to build this as ARC was to replace the final line in the .m file with:
return returnView;
figuring that the AudioUnit process that called this method would create its own copy of the returnView, and it would be released when the scope of this method ends. Using this line, and compiling as ARC, the NSView is never deallocated.
However, thinking about it now as I write, I realise that the AudioUnit calling code is not Obj-C, it's all C++, and I wasn't bridging the returned view in any meaningful way.
I am having a problem with memory management in objective C. Ive been reading through the Advanced Memory Management Programming Guide but I cannot find a solution to my problem with the possible exception of abandoning ARC altogether and managing the memory manually.
Here is the problem:
I have a Controller class that Ive made, that holds information on what to do at specific times.
The Controller class tells the rest of the app to play a video (for example). The video plays fine. When the video finishes playing, the Controller class knows what to do next.
Unfortunately the Controller class is released and deallocated by ARC almost as soon as the video starts playing. So by the time the video ends, the app calls the Controller class to see what it should do next, and the whole thing crashes. I get an EXC_BAD_ACCESS because the class is no longer in memory.
I get that ARC is releasing my Controller class because after it has told the video to start playing, its not doing anything. But I want to keep hold of that class until I need it again.
I am declaring this class as a property, like so:
#property (strong, nonatomic) Controller * controller;
But despite this, ARC keeps releasing the class as soon as its not doing anything.
EDIT:
Ive moved this property into the App Delegate. But ARC is still releasing it. I cant turn this into a Singleton, as I need the potential to have multiple copies of this class.
How can I stop ARC releasing objects when I dont want it to??
Is it possible to keep an object in memory while its not doing anything?
Is this possible at all? Or should I abandon ARC and just do memory management manually?
Use a singleton pattern so that Controller looks after its own lifetime and exists app-wide. This shared instance will exist from when it's first requested until the app terminates and ARC will not release it arbitrarily.
Controller.h:
#interface Controller : NSObject
+ (Controller *)sharedInstance;
#end
Controller.m:
#import "Controller.h"
static Controller *_instance = nil;
static dispatch_once_t _onceToken = 0;
#implementation Controller
+ (Controller *)sharedInstance {
dispatch_once(&_onceToken, ^{
_instance = [[Controller alloc] init];
});
return _instance;
}
// You could add this if you want to explicitly destroy the instance:
+ (void)destroy {
_instance = nil;
_onceToken = 0;
}
Your controller is getting dealloc'ed when the detailViewController is dealloc'ed. Hence, you must move the handle of your controller and define in it the any of the following :
MasterViewController or your application's RootViewController
OR
AppDelegate
OR
Create a singleton as answered by "trojanfoe"
I stumbled upon this case several times when working with UITableViews. I created a private #property (strong) id *yourObjectRetain and assigned my object to it. An array for multiple objects will also work.
I am having a problem with memory management in objective C. Ive been reading through the Advanced Memory Management Programming Guide but I cannot find a solution to my problem with the possible exception of abandoning ARC altogether and managing the memory manually.
Here is the problem:
I have a Controller class that Ive made, that holds information on what to do at specific times.
The Controller class tells the rest of the app to play a video (for example). The video plays fine. When the video finishes playing, the Controller class knows what to do next.
Unfortunately the Controller class is released and deallocated by ARC almost as soon as the video starts playing. So by the time the video ends, the app calls the Controller class to see what it should do next, and the whole thing crashes. I get an EXC_BAD_ACCESS because the class is no longer in memory.
I get that ARC is releasing my Controller class because after it has told the video to start playing, its not doing anything. But I want to keep hold of that class until I need it again.
I am declaring this class as a property, like so:
#property (strong, nonatomic) Controller * controller;
But despite this, ARC keeps releasing the class as soon as its not doing anything.
EDIT:
Ive moved this property into the App Delegate. But ARC is still releasing it. I cant turn this into a Singleton, as I need the potential to have multiple copies of this class.
How can I stop ARC releasing objects when I dont want it to??
Is it possible to keep an object in memory while its not doing anything?
Is this possible at all? Or should I abandon ARC and just do memory management manually?
Use a singleton pattern so that Controller looks after its own lifetime and exists app-wide. This shared instance will exist from when it's first requested until the app terminates and ARC will not release it arbitrarily.
Controller.h:
#interface Controller : NSObject
+ (Controller *)sharedInstance;
#end
Controller.m:
#import "Controller.h"
static Controller *_instance = nil;
static dispatch_once_t _onceToken = 0;
#implementation Controller
+ (Controller *)sharedInstance {
dispatch_once(&_onceToken, ^{
_instance = [[Controller alloc] init];
});
return _instance;
}
// You could add this if you want to explicitly destroy the instance:
+ (void)destroy {
_instance = nil;
_onceToken = 0;
}
Your controller is getting dealloc'ed when the detailViewController is dealloc'ed. Hence, you must move the handle of your controller and define in it the any of the following :
MasterViewController or your application's RootViewController
OR
AppDelegate
OR
Create a singleton as answered by "trojanfoe"
I stumbled upon this case several times when working with UITableViews. I created a private #property (strong) id *yourObjectRetain and assigned my object to it. An array for multiple objects will also work.
In a project I'm working on (I picked up this code and I've been trying to debug it), I have a function that gets called by an observer. The observer calls a method that updates data to be put on a screen. While this update is happening (it takes a few seconds for the updates to occur), a user can press the 'Back' button on the navigation bar, which causes a dealloc call to occur. While the method is running, the dealloc call releases all of the ivars, which eventually causes EXC_BAD_ACCESS when the method attempts to access the ivars. The structure of the update method is also enclosed with a #synchronized block.
- (void)update {
#synchronized(self){
// some code here...
// Also access ivars here.
}
}
What can be done to tell the controller to finish the method first before deallocating? I've tried running a while loop with a condition in the dealloc, but that doesn't seem efficient. It also never fully executes if the controller is released, and stays in a deadlock. I feel like the solution is simple, but my brain is fried from a long day at work and I can't think about it.
You can call retain on self to ensure the reference count does not reach zero while running a longer running method; and avoid the dealloc that way:
- (void) update {
[self retain]
// do work ...
[self release]
}
If you put the work you need to do in a block, the compiler will automatically retain all the objects referenced inside the block (including self). For example:
- (void)update {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// some code here, access ivars, do whatever you want
}];
}
The update method will immediately return and the block will be scheduled to run on the main run loop (no secondary threads are involved here).
If the update work is a long-running task, you can use a background queue instead of the main one, and then (inside that block) schedule another block to run on the main queue and interact with the UI when the work is done.
Since you are talking about the NavigationBar my guess is that what is happening is that your NavigationController is releasing its reference to your UIViewController class, which in turn is holding the ivars. One way around it is to retain the objects inside the viewcontroller until the calcuations are made, as the other answers suggest. However if the calculations you are doing are made to calculate what data should be shown in that view, the next time you open the same view, you will reload the view again from the beginning.
What I would do is that I would keep a reference to the ViewController that you are working with outside of the navigationcontroller, so that it does not get released when the user presses the back button.
Then later, if the user comes back to the same view, you will already have the data loaded. Just push it again using the same reference.
Example:
#interface YourRootViewController : UIViewController {
YourNextControllerClass *nextController;
}
#property (nonatomic, retain) YourNextControllerClass *nextController;
#end
In the ViewDidLoad of your top ViewController:
self.nextController = [[[YourNextControllerClass alloc] initWithNibName:#"YourNextControllerNib" bundle:nil] autorelease];
When you want to show the view:
[myNavigationController pushViewController:nextController animated:YES];
If the user presses the back button, the viewcontroller will not be released, so when you push it again, everything will be there as you left it.
The solution depends on whether you want to keep that object alive to the end of the method or not. However, you also use #synchronized (self): I would be very afraid what happens if self gets deallocated and the #synchronized tries to remove the lock on self, so in this case I would try to keep it alive. (However, #synchronised and then running lots of code is not a good idea in my opinion; #synchronised should be used for the smallest amount of code possible to avoid deadlocks).
With ARC, keeping the object alive within the method is simple.
typeof (self) myself = self;
will create a strong reference to self. And better to use myself in the method body instead of self.
If you don't want to keep the object "self" alive, which would be the usual case:
__weak typeof (self) weakSelf = self;
Then wherever you want to make sure that self is still there you write
typeof (self) strongSelf = weakSelf;
if (strongSelf != nil) {
}
Hi I have a nasty memory management issue under ARC and cannot figure out how to solve it. The problem stands like this, I have these objects:
ObjectManager - a singleton that does fetchRequests to core data and fetches one or more NSManagedObjects
UIViewControllerA - a view controller in which I have a button "PassManagedObject" and a property declared as below:
#property (strong, nonatomic) ManagedObject *objectForToday;
in viewDidLoad on UIViewControllerA, I call the method refreshDailyObject which does this:
self.objectForToday = nil;
self.objectForToday = [[ObjectManager sharedManager] getDailyObject];
if I tap the PassManagedObject button I create UIViewControllerB, pass the objectForToday to it and display it, see below
- (IBAction)passManagedObjectTapped:(id)sender {
UIViewControllerB *viewController = [[UIViewControllerB alloc] initWithNibName:#"UIViewControllerB"];
viewController.object = self.objectForToday;
[self.navigationController pushViewController:viewController animated:NO];
}
UIViewControllerB has a property declared like this:
#property (strong, nonatomic) ManagedObject *object;
and a button "Back" which does this:
- (IBAction)backAction:(id)sender {
self.object = nil;
[self.navigationController popViewControllerAnimated:NO];
}
Now the problem is this. If I tap continuously passManagedObjectTapped and then backAction, and again passManagedObjectTapped and again backAction and then again passManagedObjectTapped and automate this I'm getting eventually Received Memory Warning 1, and then crash.
The Instruments doesn't show any leaks but my memory allocation keeps slowly going up.
I am using ARC under iOS4.3 & iOS5. I've been struggling to figuring out what is wrong for a day now. Any help will be highly appreciated.
Thanks!
The self.object = nil; and self.objectForToday = nil; isn't necessary - ARC and the synthesized properties setters already take care of that.
It seems pretty likely that you have a circular reference somewhere. Do you have any situations where object A has a strong reference to object B and object B has a strong reference to object A?
If so, just change one of those references to weak instead (or assign if you want to support iOS 4.3).