Objective-C associated objects leaking under ARC - objective-c

I have encountered with a strange objc_setAssociatedObject behavior under ARC. Consider the following code:
static char ASSOC_KEY;
#interface DeallocTester : NSObject
#end
#implementation DeallocTester
- (void) dealloc
{
NSLog(#"DeallocTester deallocated");
//objc_setAssociatedObject(self, &ASSOC_KEY, nil, OBJC_ASSOCIATION_RETAIN);
}
#end
#implementation AppDelegate
- (void) applicationDidFinishLaunching:(UIApplication *)application
{
NSObject *test = [[DeallocTester alloc] init];
objc_setAssociatedObject(test, &ASSOC_KEY, [[DeallocTester alloc] init],
OBJC_ASSOCIATION_RETAIN);
}
I'm creating an instance of DeallocTester, then I set another DeallocTester as an associated object for it, then both of them go out of scope.
I expect the -dealloc of the first object to be called, then the associated object to be deallocated too, but I see the "DeallocTester deallocated" message printed only once. If I uncomment the objc_setAssociatedObject line in -dealloc, the second object gets deallocated too.
The Objective-C reference states that associated objects are deallocated automatically upon object destruction. Is it a compiler/ARC/whatever issue or am I missing something?
Update
This sample code is actually working if you run it from a brand-new project. But I have two ARC-enabled projects where it doesn't. I'll do some investigation and provide a better sample.
Update 2
I've filled a rdar://10636309, Associated objects leaking if NSZombie objects enabled in ARC-enabled project

I've found a source of a problem - I had NSZombie objects enabled in both my projects where this bug appears.
As far as I understand, when zombie objects are enabled, the normal instances are replaced with NSZombie upon deallocation, but all the associated objects are left alive! Beware of that behavior!
I've created a rdar://10636309
Update: There's a workaround by Cédric Luthi, and this issue appears to be fixed in iOS 6.

The code you posted works exactly as advertised under ARC. I rewrote your dealloc implementation to help make things a little more obvious.
- (void)dealloc
{
NSLog(#"deallocating %#", self);
}
Here's the resulting log:
2012-01-03 06:49:39.754 ARC Stuff[47819:10103] deallocating <DeallocTester: 0x6878800>
2012-01-03 06:49:39.756 ARC Stuff[47819:10103] deallocating <DeallocTester: 0x688b630>
Are you sure you're compiling with ARC enabled?

Related

In Objective-C under MRC if an object gets de-alloced do objects it created get de-allocated as well?

This may look like a dup of this, but I don't think that answers my question as it is about associated objects, not objects that were created by and whose only pointer resides within an object.
Let's say I had this example in MRC mode.
// In h file
#interface MyViewController : UIViewController {
NSObject* myNsObject;
}
// In m file
-(void) viewDidLoad() {
myNsObject = [[NSObject alloc] init]; // I'm never going to release myNsObject
}
I'm smart enough to release myViewController correctly. It's reference count goes to zero and it is de-allocated. But I never released myNsObject, so it had been hanging around with a reference count of 1. So would a release, and therefore de-alloc, automatically get done on myNsObject? Or would myNsObject get leaked in that case?
The proper memory management here is to release myNsObject in the dealloc method of the view controller:
- (void)dealloc {
[myNsObject release];
[super dealloc];
}
If you create something then you are responsible for releasing it (under MRC).
Failure to do this results in memory leaks.

Using ARC for the Cocoa UI of an AudioUnit prevents NSView dealloc from being called

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.

NSTableViewDataSource dealloc in objective-c

I'm currently learning objective-c and I'm currently training with NSTableView.
Here is my problem :
I have linked my tableview to my controller through Interface Builder so that it has a datasource, I have implemented NSTableViewDataSource protocol in my controller and I have implemented both -(NSInteger) numberOfRowsInTableView: and -(id) tableView:objectValueForTableColumn:row: methods.
I have created a raw business class ("person") and I succeeded to display its content into my NSTableView.
But then, I put some NSLog in my dealloc methods to see whether the memory was freed or not and it seems that my array as well as my "person" instances are never released.
here is my dealloc code in the controller:
-(void)dealloc
{
NSLog(#"the array is about to be deleted. current retain : %d",[personnes retainCount]);
[personnes release];
[super dealloc];
}
and in my "person" class
-(void) dealloc
{
NSLog(#"%# is about to be deleted. current retain : %d",[self prenom],[self retainCount]);
[self->nom release];
[self->prenom release];
[super dealloc];
}
When these deallocs are supposed to be called in the application lifecycle? Because I expected them to be called at the window closure, but it didn't.
In the hope of beeing clear enough,
Thanks :)
KiTe.
I’m assuming you’re never releasing the window controller object that owns the (only) window. As such, the window controller and every top level object in the nib file are retained throughout the application lifecycle, including the window (and its views).
Since the window controller exists throughout the application lifecycle, it isn’t released, hence its -dealloc method is never called. And, since the controller -dealloc method is never called, its personnes array isn’t released.
The personnes array owns its elements. Since the array isn’t released, neither are its elements, hence the -dealloc method of the corresponding class/instances is never called.
Don't ever use retainCount. The results are misleading at best. If you practice proper memory management practices, you'll be fine. Have you had any memory issues/crashes?

What is dealloc in objective C

I want to ask an general question about the objective C. When I write the program of the iPhone application, I always see a function called 'dealloc' in the .m. when will this method be called? do I need to put all the [release] in here good for the application? thank you very much.
// ------------------ updated content -------------------------
NSArray *arr;
NSString *str;
NSMutableArray *mutableArr;
// in the dealloc
// it should have to following
[arr release];
[str release];
[mutableArr release];
the function will be call 3 times?
The dealloc method is called on an object when it's retain count has reached zero. Retain counts are increased by one for each retain call, and reduced once for each release call. The autorelease schedules a future release call when the current NSAutoreleasePool is drained, typically at the end of an event cycle, but you can set up your own NSAutoreleasePools on memory intensive operations. (See the NSAutoreleasePool docs for details.)
What should you put into dealloc? You should put a release for each member object the object of that class retains.
A couple things make this easier. The nil object will quietly ignore any messages sent to it, so [foo release] when foo = nil is not a bug. However, releasing an object twice can cause serious issues. My (hardly unique) solution to this is to explicitly set whatever I just released to nil, whenever I release it. In fact, I put the nil assignment on the same line as the release so I can grep for "release" and find places I missed. Example:
#interface MyClass {
Foo *foo;
Bar *bar;
NSInteger baz;
}
-(void)dealloc;
#end
#implementation MyClass
-(void)dealloc {
[foo release]; foo = nil;
[bar release]; bar = nil;
[super dealloc];
}
#end
I'll assign nil to a variable even when that variable is about to go out of scope or the object is about to go away. Why? If another object of the same class is allocated in the same memory space after I've released this one, it guarantees there will be no dangling pointers the new object might accidentally use and make debugging a nightmare. (See also NSZombieEnabled for debugging help.)
when will this method be called?
It's called when the reference count for that object becomes 0 because all its pointers have been released. The memory taken up by it is deallocated (freed); the object itself is destroyed.
do I need to put all the [release] in here good for the application?
Yes, release all the properties of the object that are still retained.
EDIT: in response to your updated question, dealloc in your custom object is only called once. It will then send these three messages:
[arr release];
[str release];
[mutableArr release];
To each of the three objects. They are entirely different objects, but if you only have one instance of each, then their reference counts all go down to 0 and their dealloc methods are called automatically.
As you surmised, it's called when an object is destroyed. The object should release everything it owns.
In Other words, Dealloc frees/destroys/releases the memory you allocated to your objects.
Allocated objects to memory should be destroyed once not used to avoid memory leaks that might cause your application to crash.
ZaldzBugz

UINavigationViewController memory releasing

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)?