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!
Related
I'm getting a really weird bad access error while using dispatch async. I managed to reduce it down to this segment of code in my program.
-(void)buttonTapped:(id)sender {
__block NSArray*foo = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Foo was initially declared here but then moved it outside.
foo = [self someMethod];
[foo retain]; // bad access here. Why ?
dispatch_async(dispatch_get_main_queue(),0) {
// doesnt matter what happens here
}); });
}
-(id)someMethod
{
return [self secondMethod];
}
-(id)secondMethod
{
// was initially returning an autoreleased object from here. Changed it
// to eliminate that as source of the error.
id newThing = [[NSObject alloc] init];
return newThing;
}
The code didnt initially look like this but this is how it is right now . Including allocating a dummy NSObject .
How is it possible for foo to get released in between calls inside a dispatch async ? I dont understand how this is possible. I know its difficult to suggest whats going from just this but any debugging suggestions would be helpful. I tried turning on NSZombies but I dont get any Zombies.
You ask:
How is it possible for foo to get released in between calls inside a dispatch_async?
It shouldn't, unless someMethod or secondMethod are, themselves, doing something asynchronously which might allow the autorelease pool to be drained in the interim.
I tried turning on NSZombies but I dont get any Zombies.
If you've got zombies turned on and you're not getting a zombie, then I suspect the problem rests elsewhere. Frankly, I suspect that the root of the problem was eliminated in your process of simplifying the sample code for the purposes of the question:
A few other observations/clarifications:
You declared foo to be a NSArray, but then you're returning NSObject. I'll assume you meant it to be NSObject throughout.
You have a line of code that says:
dispatch_async(dispatch_get_main_queue(),0) {
I'll just assume that was a typo and that you intended:
dispatch_async(dispatch_get_main_queue(), ^{
The foo variable should definitely be inside the dispatch_async block. It doesn't really make sense to have a __block variable for something (a) you don't reference outside of that block for a block; and (b) for a block you're dispatching asynchronously.
The secondMethod should return an autorelease object, as you apparently originally had it. (Or you'd probably want to change secondMethod and someMethod to start with new in their names to avoid confusion and make life easier for yourself when you eventually move to ARC.)
If you retain the foo object, you'll want to also add the appropriate release. In fact, your original code sample returns a +1 object, and then retain it again, bumping it to +2, so you'd need two release calls.
Anyway, correcting for these various issues, I end up with the following, which does not generate an exception:
- (IBAction)buttonTapped:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSObject *foo = [self someMethod];
[foo retain]; // no bad access here
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"foo = %#", foo);
[foo release];
});
});
}
- (NSObject *)someMethod
{
return [self secondMethod];
}
- (NSObject *)secondMethod
{
return [[[NSObject alloc] init] autorelease];
}
Furthermore, I would suggest, especially when using manual retain and release (MRR), that you run it through the static analyzer ("Analyze" on the Xcode "Product" menu) and make sure you have a clean bill of health. (It would have pointed out some of the issues I mentioned.) It's not perfect, but it's remarkably good at identifying issues.
But, in short, the above code is fine, and if you're still getting an exception, update your question with working code that reproduces the exception.
I have stumbled upon an issue for which I can't find answer elsewhere. When I am calling a method which returns pointer to an object which is later used and at the end set to nil, it is still allocated in memory (according to Instruments). I'm using XCode 4.6.3 and iOS 6.1. ARC is turned on.
Here is sample code:
ClassA.h
#interface ClassA : NSObject
-(void)runSomething;
#end
ClassA.m
#import "ClassA.h"
#import "ClassB.h"
#implementation ClassA
-(void)runSomething {
int counter = 0;
while (true) {
ClassB *instance = [self makeClassBWithNumber:counter];
NSLog(#"%d", [instance getNumber]);
[NSThread sleepForTimeInterval:0.01];
instance = nil;
counter++;
}
}
-(ClassB*) makeClassBWithNumber:(int)number {
return [[ClassB alloc] initWithNumber:number];
}
#end
ClassB.h
#interface ClassB : NSObject
#property int number;
-(id)initWithNumber:(int)number;
-(int)getNumber;
#end
ClassB.m
#import "ClassB.h"
#implementation ClassB
-(id)initWithNumber:(int)number {
self = [super init];
if(self) {
_number = number;
}
return self;
}
-(int)getNumber {
return [self number];
}
#end
ClassB is created in view controller and method runSomething is called. This sample code produces that created object (ClassB) is never released from memory. If I change code from
ClassB *instance = [self makeClassBWithNumber:counter];
to
ClassB *instance = [[ClassB alloc] initWithNumber:counter];
created object is properly released in each of loop cycle. What is the reason for such behaviour? I found some old answers here on stackoverflow that makeClassBWithNumber should return result invoking autorelease return [result autorelease], but this can't be done if ARC is enabled.
The difference is that +alloc returns an object with a +1 retain, which ARC will balance with a release at the end of its scope, and so immediately deallocate. +make… returns an object with a +1 retain and a matching autorelease. The autorelease pool will send a release message when it drains. Since you stay in loop "while true," the autorelease pool never drains and you accumulate memory.
The solution is to give your loop an autorelease pool:
while (true) {
#autoreleasepool { // <== Add an autorelease block here.
ClassB *instance = [self makeClassBWithNumber:counter];
//NSLog(#"%d", [instance getNumber]);
NSLog(#"%d", [instance number]); // Fix naming; do not prefix accessors with `get`
[NSThread sleepForTimeInterval:0.01];
// instance = nil; // Does nothing in this loop.
counter++;
}
}
This will cause the pool to drain on every iteration. In any case the instance=nil is unnecessary.
EDIT: Do read MartinR's answer. It gives some more details on the implementation details, and particularly why this may behave differently depending on the optimization level, and whether the called method is in the same compile unit (.m file) as the calling method. That is only an optimization detail; you still need to put this #autoreleasepool in the loop for correctness.
makeClassBWithNumber returns an autoreleased object, even with ARC.
(More precisely, it can return an autoreleased object, depending on the optimization.)
The difference to manual reference counting is that the ARC compiler inserts the autorelease call where required, not you.
From the Clang/ARC documentation:
3.2.3 Unretained return values
A method or function which returns a retainable object type but does
not return a retained value must ensure that the object is still valid
across the return boundary.
When returning from such a function or method, ARC retains the value
at the point of evaluation of the return statement, then leaves all
local scopes, and then balances out the retain while ensuring that the
value lives across the call boundary.
In the worst case, this may involve an autorelease, but callers must not assume that the value is
actually in the autorelease pool.
makeClassBWithNumber is not a alloc, copy, init, mutableCopy, or new
method and therefore returns an unretained return value.
The operative word in your question is "OLD". Old answers are no longer relevant.
This is what ARC is for.
You no longer need to worry about any memory management.
If ARC tells you not to do it... don't.
In this case, you don't need autorelease.
As others have said the difference you are seeing is down to whether a method returns and object the caller owns or an object the caller does not own.
In the former category are methods in the alloc, init, new, copy & mutableCopy categories. These all return an object owned by the caller and ARC will ensure it is released.
In the latter category are all the methods not in the first! These return an object which is not owned by the caller, ARC will ensure that this object is retained if needed and released if it was retained. Return values in this category may be in the auto-release pool, which means they will live at least as long as they are in the pool (maybe longer if they've been retained by ARC) and the standard pool is emptied at the end of each run loop cycle. This means that in loops which generate a lot of entries into the auto-release pool that a lot of no longer needed objects can accumulate in the pool - this is what you are seeing.
Under MRC the solution was to introduce a local auto-release pool, within such loops, to avoid accumulation of no longer needed objects. However under ARC this is probably not the best choice.
Under ARC the better solution is probably to follow the naming conventions, and in this case you want to use the new pattern - new is the standard pattern for alloc + init in one method. So rename your makeClassBWithNumber as newClassBWithNumber:
// *create* a new ClassB object
- (ClassB *) newClassBWithNumber:(int)number
{
return [[ClassB alloc] initWithNumber:number];
}
This indicates the method returns an object the caller owns, it is a "creation" method, and ARC will handle the rest without no longer objects accumulating.
(Adding a newWithNumber method to ClassB itself is often a good idea under ARC.)
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;
}
Given the following simple implementation:
#implementation RTUDeallocLogger
-(void)dealloc
{
NSLog(#"deallocated");
}
#end
we run the following code under ARC:
#implementation RTURunner
{
NSArray* arr;
}
-(void)run{
arr = [NSArray
arrayWithObjects:[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
nil];
NSLog(#"nulling arr");
arr = NULL;
NSLog(#"finished nulling");
}
#end
we get the following log output:
nulling arr
finished nulling
deallocated
deallocated
deallocated
I'd like to perform an action after all the deallocations have finished. Is this possible?
The aim of this question is really to understand a little more about the mechanics of ARC, in particular, at what point ARC triggers these deallocations, and whether or not this can ever happen synchronously when I drop references.
-dealloc is always synchronous, and occurs when the last strong reference is removed. In the case of your code though, +arrayWithObjects: is likely (if compiled at -O0 at least) putting the array in the autorelease pool, so the last strong reference is removed when the pool drains, not when you set the variable to NULL (you should use nil for ObjC objects, btw).
You can likely avoid having the object in the autorelease pool by using alloc/init to create, and you may (implementation detail, bla bla) be able to avoid it by compiling with optimizations turned on. You can also use #autoreleasepool { } to introduce an inner pool and bound the lifetime that way.
If I were an engineer from Apple I'd probably argue that your problem is probably your design. There are almost no reasons you'd want effectively to act by watching dealloc rather than having dealloc itself act.
[a huge edit follows: weak properties don't go through the normal property mechanisms, so they aren't KVO compliant, including for internal implicit KVO as originally proposed]
That said, what you can do is bind the lifetime of two objects together via object associations and use the dealloc of the latter as a call-out on the dealloc of the former.
So, e.g.
#import <objc/runtime.h>
#interface DeallocNotifier;
- (id)initWithObject:(id)object target:(id)target action:(SEL)action;
#end
#implementation DeallocNotifier
- (id)initWithObject:(id)object target:(id)target action:(SEL)action
{
... blah ...
// we'll use a static int even though we'll never access by this key again
// to definitely ensure no potential collisions from lazy patterns
static int anyOldKeyWellNeverUseAgain;
objc_setAssociatedObject(object, &anyOldKeyWellNeverUseAgain, self, OBJC_ASSOCIATION_RETAIN);
... blah ...
}
- (void)dealloc
{
[_target performSelector:_action];
}
#end
-(void)run{
arr = ...
[[DeallocNotifier alloc]
initWithObject:arr target:self action:#selector(arrayDidDealloc)];
/* you may not even need *arr in this case; I'm unclear as
to why you have an instance variable for something you don't
want to keep, so I guess it'll depend on your code */
} // end of run
- (void)arrayDidDealloc
{
NSLog(#"array was deallocated");
}
I've assumed you're able to tie the lifecycle of all the objects you're interested in to that of a single container; otherwise you could associate the notifier to all relevant objects.
The array has definitely gone by the time you get arrayDidDealloc.
at what point ARC triggers these deallocations
ARC inserts allocations/deallocations into your code based on static analysis. You can see where it does this by looking at the assembly of your source -- go to Product -> Generate Output in Xcode.
whether or not this can ever happen synchronously when I drop references
Retain/release/autorelease is always synchronous.
Your code
arr = [NSArray arrayWithObjects:[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
nil];
will be implicitly placing the objects into an autorelease pool. After the object is allocated, you don't want it retained (because the NSArray will do the retain once it receives the object), but you can't release it immediately, otherwise it will never make it to the NSArray alive. This is the purpose of autorelease - to cover the case where the object would otherwise be in limbo between two owners.
The retain count at alloc time is 1, then it's retained by the autoreleasepool and released by you, so the retain count remains 1. Then, it's retained by the NSArray, so the retain count becomes 2.
Later, the NSArray is released and so the retain count returns to 1, and the objects are finally cleaned up when the autorelease pool gets its chance to run.
You can make the autorelease act faster by nesting another pool - by wrapping your NSArray creation with an #autorelease{} clause.
I have a view controller that I want to lazily initialize, and once initialized, use the same copy when possible (I don't use a singleton since I do want to remove it from memory eventually), I use the getter to do so, my code look like this:
#property (retain) UIViewController *myController
...
#synthesize myController = _myController;
...
- (UIViewController *)myController
{
if (!_myController) { // Evaluation
_myController = [[MyViewController alloc] init]; // Object Creation
}
return _myController;
}
This works, but it's not thread safe, and if more than one thread evaluate to true before the object is created, I'll have a memory leak. One solution I've tried is to #synchronized the code, but I'm not sure the correct way to do it.
This appears to work, (lockForMyController is a simple NSString) but it makes this section of code a lot slower:
- (UIViewController *)myController
{
#synchronized(self.lockForMyController){
if (!_myController) {
_myController = [[MyViewController alloc] init];
}
}
return _myController;
}
I was wondering if there is some other way to achieve a lazy initialized, thread safe, property?
This solution works
Note that this solution only works if myController is accessed on a background thread the first time. It will deadlock if called on the main thread.
You want to use gcd. The key is serialize the creation of the object, so that regardless of the threads starting the block, it will always only be created exactly once.
- (UIViewController *)myController
if (_myController == nil) {
dispatch_sync(dispatch_get_main_queue(), ^ { if (_myController == nil) _myController = [[MyViewController alloc] init]; });
}
return _myController;
}
Here, even if multiple threads execute the block, the execution of the block is serialized onto the main thread and only one MyViewController can ever be created.
You won't see a performance hit here unless the object is nil.
Since the property is implicitly atomic, that means that in the setter the value will be autoreleased. This should make it suitable for mingling with your custom getting, since it will autorelease any value changes to _myController.
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW2
However, you still may get into a race condition where you are setting the value on one thread but accessing it on another. Any time you set the value, you probably want to make sure and do something like this:
dispatch_sync(dispatch_get_main_queue(), ^ { self.myController = {newValueOrNil} });
This will make sure to serialize your setter methods calls without having to reinvent the wheel for atomic setters, which is very hard to get right.
This solution does not work
You want to use gcd.
http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_once
See this post about singletons. I know you don't want a singleton, but this demonstrates how to use the method. You can easily adapt it.
Create singleton using GCD's dispatch_once in Objective C