What I've found using the profiling tool in Xcode (Allocations) is that when you nil out a property it does not get deallocated until the parent class gets nilled out. Now let's say you want to make sure that you don't keep an expensive modal view controller in memory (assuming that it won't get used very often), if the expensive VC is a property, the allocated memory for that property will not get released when the property gets nilled out, which means that when the user wants to use the expensive VC again we'll allocate the same amount of memory each time. This is easy to spot in the profiler as the graph just keeps climbing.
However, if I only define the expensive VC as an instance variable and define my own setter&getter the profiler allocation graph actually decreases immediately when the variable gets nilled out and goes back up with the same amount upon each new allocation.
So my question is, why does a variable 'seem' to get deallocated when defined as an instance variable but not when defined as a property?
// What I call defining something as an instance variable:
#interface SomeViewController ()
{
UIPopoverController *somePopover;
}
// What I call defining something as a property
#property (nonatomic,strong) UIPopoverController *somePopover;
// Nilling out a property which does not get allocated unless it does not have a parent (root node memory graph wise)
self.somePopover = nil;
// Nilling out an instance variable which does make the memory graph in the profiler go down by the same amount it went up
somePopover = nil;
AFAIK, you cannot force an object to release all of its memory until its parent calls deallocate whereupon all of its children get cascade deallocated..
https://stackoverflow.com/a/7631831/2536815
I don't think your analysis is correct. If properly used, both a property and an instance variable have the same effect on reference counting: setting it to nil decreases the reference counter and if it goes to 0, the instance (be it a view controller or something else) is immediately deallocated.
If this is not the case in your app, then the cause must be something else than property vs. instance variable.
To further analyze your specific problem, we'll need more information. So please post your code, describe the set up for measuring the memory management effects, what the effective results are and what you would expect instead.
So it seems like I just confused hiding/showing an expensive view with allocating/nilling, the memory graph goes down when the view isn't visible and vice versa, really silly of me I know..
Just to verify that you cannot force arc to release a property/ivar I created a new xcode project where I just put the code below in the didFinishLaunching method. And unfortunately the memory allocated for the property and ivar are kept in memory no matter if I nil them out or not. In my case the code below generates 2.8 MB of memory and when this method finishes and the application launches the profiler memory graph stays at 2.8MB indefinitely..
#interface SSAppDelegate ()
{
NSMutableArray *ivVC;
}
#property (nonatomic, strong) NSMutableArray *propertyVC;
#end
#implementation SSAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
for (int k=0; k<10; k++) {
ivVC = [NSMutableArray array]; // Doesn't matter if it's alloc] init]
self.propertyVC = [NSMutableArray array];
for (int i=0; i<1000;i++) {
[ivVC addObject:#"..................................................................................."];
[_propertyVC addObject:#"..................................................................................."];
}
ivVC = nil;
self.propertyVC = nil;
}
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Related
I'm new to ARC but understand how it works and I'm trying it out. I'm on iOS so memory is a severe concern.
I have a MyObject class which contains lots of big data. I want to release it, and load a new set of data.
MyObject *object;
object = [[MyObject alloc] initWithData:folder1]; // load data from folder1
// later...
object = [[MyObject alloc] initWithData:folder2]; // load data from folder2
This works fine without leaks, and I'm guessing the ARC inserts a [object release] before the new assignment. My problem is the data inside 'object' is released after the new set is allocated, and I run out of memory. What I really want to be able to do is:
object = nil;
<function to pop the pool, wait till everything is deallocated>
object = [MyObject alloc] initWithData:folder2]; // load data from folder2
but I'm not sure how to do that. I could run the new allocation on a performselector afterdelay, but it feels like I'm shooting in the dark and a bit of hack. There's probably a proper way to do this?
P.S I've tried searching for an answer, but all results are about memory leaks and how to make sure variables go out of scope and set variables to nil etc. My issue isn't about that, it's more of a timing thing.
UPDATE
Thanks for the answers, I'd already tried
object = nil;
object = [MyObject alloc] initWithData:folder2];
and it hadn't worked. I wasn't sure whether it was supposed to or not. Now I understand that it is supposed to work, but I must have something else holding on to it for that fraction of a second. I have NSLogs in all of my init/dealloc methods, and I can see first all the inits of the new instances of classes (of MyObject's ivars) being called, and then almost immediately after (within a few ms), the dealloc of MyObject, followed by the deallocs of its ivars.
I also tried the #autorelease but the same thing happens.
I've searched throughout the project and pasted all the code which I think may be relevant to this.
#interface AppDelegate : UIResponder <UIApplicationDelegate>;
#property PBSoundSession *soundSession;
#end
//--------------------------------------------------------------
#implementation AppDelegate
// onTimer fired at 60Hz
-(void)onTimer:(NSTimer *) theTimer {
[oscReceiver readIncoming]; // check incoming OSC messages
// then do a bunch of stuff with _soundSession;
}
#end
//--------------------------------------------------------------
#implementation OscReceiver
-(void)readIncoming {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
// parse all incoming messages
if(bLoadNewSoundBank) {
NSString *newFolder = parseNewFolder();
appDelegate.soundSession = nil;
appDelegate.soundSession = [MyObject alloc] initWithData:newFolder];
}
}
#end
//--------------------------------------------------------------
#implementation GuiController
// onTimer fired at 10Hz
-(void)onTimer:(NSTimer *) theTimer {
PBSoundSession *soundSession = appDelegate.soundSession;
// update gui with received values
}
#end
I thought it might be the 'soundSession' local variable in the GuiController::onTimer holding onto the old appDelegate.soundSession for the duration of that method, but to my surprise commenting out all of the GUI code (in fact disabling the timer), made no difference.
Is there a way of finding out at that point who is still holding onto my appDelegate.soundSession? I placed a breakpoint where I set it to nil, but couldn't find any useful information. I tried Instruments in Allocation template, but couldn't find anything useful there either (probably because I don't know where to look).
This is what my allocations track looks like, you can see the memory is all deallocated a bit too late!
.
This might not be an an ARC problem. What you could be seeing is your autorelease pool not draining soon enough—your MyObject is getting released, but the data it loaded is getting held onto by the pool because of some internal -retain/-autorelease pair. Try wrapping your -initWithData: calls in an #autoreleasepool block, like this:
#autoreleasepool {
object = [[MyObject alloc] initWithData:folder1];
// do things
}
// later…
#autoreleasepool {
object = [[MyObject alloc] initWitData:folder2];
// do other things
}
Setting the object to nil immediately before setting it to something else as Gabriele suggests might cause the compiler to insert the appropriate release before the second -alloc/-initWithData:, but it might be smart enough to do that already—if that doesn’t work, it’s most likely the autorelease-pool thing.
There is no delay when draining an #autoreleasepool {...}; the objects in the pool have release invoked immediately. If an object survives that, it is because there is either a strong reference elsewhere or because the object was autoreleased into the next pool out.
If you do:
a = [[Foo alloc] initBigThing];
a = nil;
a = [[Foo alloc] initBigThing];
The first instance of Foo will be released prior to the allocation of the second
With one big caveat; if any of the code paths that a is invoked upon happen to retain/autorelease it, then it'll stick around until the pool is drained. Surrounding it in #autoreleasepool{ ... }; should do the trick.
Note that the compiler will sometimes emit retain/autorelease sequences in non-optimized builds that are eliminated in optimized builds.
A bit more general answer, I found how you can force release an object:
#import <objc/message.h>
// ---
while ([[object valueForKey:#"retainCount"] integerValue] > 1) {
objc_msgSend(object, NSSelectorFromString(#"release"));
}
objc_msgSend(object, NSSelectorFromString(#"release"));
But you shouldn't do this because ARC will probably release the object later and this will cause a crash. This method should be only used in debug!
I haven't done any iOS development since iOS 3, so my memory is a little hazy (though memory management was never anything I struggled with and my mind is quite clear on that).
I'm starting a new project and don't understand why the skeleton code is structured the way it is:
- (void)dealloc
{
[_window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc]
initWithFrame:[[UIScreen mainScreen] bounds]]
autorelease];
// ... snip ...
}
Why would the window object be autoreleased? I'm pretty sure it never used to be this way in older iOS versions.
Where does _window come from? Is this just another way to access [self window]?
I'd have written this as:
- (void)dealloc
{
[self.window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc]
initWithFrame:[[UIScreen mainScreen] bounds]];
// ... snip ...
}
It was always drummed into me never to release an autoreleased object, and in fact doing so usually results in a segmentation fault.
In you second example you are leaking the window object since, the alloc will give the object a retain count of 1, the you assign it _window via the property which will also retain the object assigned to it.
It's true you should not release an autorelease object, but in the dealloc you are releasing the iVar for the window property. You should always release any property that is declared as either retain or strong. (although not when using ARC).
The _window is now automatically generated as the iVar for the property window.
There some believes that you should not use self. properties in init or dealloc.
Thus the way I do it is:
[_window release], _window = nil;
This wil set the _window to nil after releasing it, making sure that if any other thread might want to use this it will be calling on nil. Which could prevent a crash but could alo create some weird behavior. This is totally up to you.
You should move to ARC, which is a complier option to add release/autolease at compiletime fro you. There is no need to added these your self if you set the property correctly when using ARC.
So I figured this out (mostly). The #property declaration has the strong attribute. Apparently this is new to ARC (which I'm not actually using) and is otherwise just an alias for retain, so self.window = [ ... ] retains the object, hence the autorelease.
Still not clear on the _window variable, but I assume it is just a shortcut.
Check what the window property memory descriptor is like. I assume it's strong/retain in which case when you set the window property, it's value is retained thus needs to be released in dealloc.
Following your code path.
[UIWindow alloc] = retainCount of 1.
autoreleasing = retainCount of 0
setting the self.window bumps the retainCount which now = 1 and is alive until...
in dealloc you release it, thus retainCount = 0 and the object is deleted
You probably missed out that in later iOS SDKs, auto synthesized properties automatically create instance variables with underscore prefixes. So you could also do self.window = nil in your dealloc.
1. Why would the window object be autoreleased? I'm pretty sure it never used to be this way in older iOS versions.
If window object is not autoreleased, then it results in leak. Since you are using
self.window = [[[UIWindow alloc] ....]autorelease];
self. syntax allows setter function to get called, which retains the object once. So the actual window object which we alloced using [UIWindow alloc] should be released, hence autorelease. The retain using self. syntax is conceptually released in dealloc. So for one alloc plus one retain we are releasing twice.
Where does _window come from? Is this just another way to access
[self window].
Well this question is already discussed here
I am in the process of optimizing my app and making sure memory management is properly implemented. As I found the didUnload / dealloc / willAppear not reliable for implementing my memory cleanup, I decided to implement my own method so I can have full control of this memory management.
Definition of my arrays in the header file
#property (retain) NSMutableArray *selectedCardIDs;
#property (retain) NSMutableArray *selectedRowArray;
#property (retain) NSMutableArray *cardArray;
#property (retain) NSMutableArray *cardIDArray;
Here the release method:
- (void) willReleaseObjects {
[self.aCopyOfCardIDArray release];
[self.listOfItems release];
[self.aCopyListOfItems release];
[self.selectedCardIDs release];
[self.selectedRowArray release];
[self.cardArray release];
[self.cardIDArray release];
}
The arrays can get very large (> 1'000 entry each), why a release of those arrays is essential after the view is unloaded. I explicitly call this function in the IBAction method, such as:
- (IBAction) stopDictionary:(UIBarButtonItem *) sender {
[self closeDatabase];
[self willReleaseObjects];
[self dismissModalViewControllerAnimated:YES];
}
I debugged the code and it is actually executing each release, however when I diagnose the memory allocation with Instruments, it seems to free up only partial memory, actually almost nothing why the memory consumption of course is increasing every time I load this view again, which is no good at all.
Any idea, why my memory is not released here? Thanks.
Don't call release on a property as you risk something very bad happening in case you have any properties declared as assign or copy. You could potentially release a returned instance which is already autoreleased.
Instead either release the instance variable behind it or set the property to nil. Either way you will achieve the correct result. In case you have any Key/Value observers on your properties the best way is to set the properties to nil which will automatically propagate the values to any observers:
- (void) willReleaseObjects {
self.aCopyOfCardIDArray = nil;
self.listOfItems = nil;
...
}
This example is taken from The Big Nerd Range iPhone book (page 143/144) - ItemsViewController is a subclass of UITableViewController:
#interface HomepwnerAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
ITemsViewController* itemsViewController;
}
....
itemsViewController = [[ItemsViewController alloc] init];
[window setRootViewController: itemsViewController]
My question is why is it necessary to have the iVar itemsViewController, why not just do this instead:
...
window.rootViewController = [[ItemsViewController alloc] init];
I presume the window will destroy its rootViewController when the app exits and thus there's no leaks, and the window will be in existence for the lifetime of the app so I don't understand why this and many other example have a separate iVar for the root controller?
TIA
The biggest advantage is simply that you can access the methods of your view controller without needing to cast over and over again:
[itemsViewController doSomething];
// vs.
[(ItemsViewController *)window.rootViewController doSomething];
Depending on the app you might need to refer to the root view controller frequently from the app delegate, for example when implementing the handlers for entering background/foreground and similar app delegate callbacks.
There is absolutely no need to keep the ivar around if you don't need it.
BTW, you will leak ItemsViewController if you don't autorelease it. (Unless you are using ARC)
The reason is historical, I think. Back when that book was written, the window and root view controller both used to be IBOutlets and were set from a nib file called MainWindow.nib.
Also, UIWindow didn't used to have a rootViewController property to assign the control to (the root view controller.view was just added directly as a subview to window), so if you didn't store it in an ivar then it wouldn't be retained by anything and your app wouldn't work because the root view controller would be released as soon as it was created.
These days however, since iOS4 and now ARC, the base project template has been updated and doesn't even have ivars any more (which are no longer needed). It does still have an #property for the view controller, but it's technically not needed any more either, and your alternative solution of assigning a new controller directly to the window.rootViewCOntroller would work fine.
This is totally stylistic choice. There are other ways to get the convenience accessor. I don't ever create an ivar in my rootViewController never changes. I will usually go for a read only property.
#property (nonatomic, readonly) MyRootViewController *rootViewController;
- (MyRootViewController *)rootViewController {
if ([self.window.rootViewController isKindOfClass:[MyRootViewController class]) {
return (MyRootViewController *)self.window.rootViewController;
}
return nil;
}
I was wondering how the autorelese works on the iPhone. I though that once you send an autorelease to an object it is guaranteed to be retained in till the end of the scope of the block the autorelease was sent. Is that correct?
I was initializing a view from a NIB in the applicationDidFinishLaunching like below:
(void)applicationDidFinishLaunching:(UIApplication *)application {
loginViewController = [[[LoginViewController alloc] initWithNibName:#"LoginView" bundle:nil] autorelease];
[window addSubview: [loginViewController view]];
[window makeKeyAndVisible];
}
and the view did not show at all, all there was on the screen was the UIWindow
Now once I removed the autorelease from the end of the controller initialization all went smooth from there on.
What is this about?
Cheers,
K.
When you call autorelease, you give ownership of the object to the current autorelease pool. The run loop creates a new autorelease pool before it dispatches an event (such as applicationDidFinishLaunching:) and destroys that pool when the event finishes.
When you give ownership of your LoginViewController to the autorelease pool, it gets released just after the applicationDidFinishLaunching: returns. When the view controller deallocates itself, it removes its view from the superview (your window in this case).
Your application delegate should keep ownership of the LoginViewController and release it in the app delegate's dealloc method (or when you're done with your login and have moved on to another view).
To expand on Don's answer, it may be somewhat confusing to say "you give ownership of the object to the current autorelease pool." This might be misunderstood to mean the object is guaranteed to be destroyed when the autorelease pool is drained. This is not correct (though it will happen in this case). Sending -autorelease requests that the autorelease pool send a -release message when it is drained. If that -release message makes retainCount = 0, then the object will be destroyed.
So, in order to do what Don is recommending, you need to create a ivar to keep track of this view controller. His explanation of why the view vanishes is exactly right; but you don't want to just leak the view controller. You want to hold onto it, and release it when you're done with it.
#interface ... {
LoginViewController *_loginViewController;
}
#property (readwrite, retain) LoginViewController *loginViewController;
#implementation ...
#synthesize loginViewController = _loginViewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
self.loginViewController = [[[LoginViewController alloc] initWithNibName:#"LoginView" bundle:nil] autorelease];
[window addSubview: [loginViewController view]];
[window makeKeyAndVisible];
}
- (void)dealloc {
[_loginViewController release]; _loginViewController = nil;
[super dealloc];
}
The autoreleasepool is cleaned at the end of the runloop. This means as long as you invoke methods and do stuff, it's still there.
I don't see the error in your code, but the Window is retained properly in your example.
Since you're adding your LoginViewController to the autorelease pool it's being released at the end of the run loop. When that happens it also releases its' view and removes it from being displayed.