9

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

iHunter
  • 6,205
  • 3
  • 38
  • 56
  • If you are using LLVM3+ why are you using associated objects? You can just create iVars and properties in categories on the class instead. – Abizern Jan 03 '12 at 12:39
  • @Abizern I'm going to add ivars to `NSObject`, AFAIR ivars can be added to a class extension category only, not to arbitrary one. – iHunter Jan 03 '12 at 12:52
  • @Abizern Really? How do you create an ivar in a category? – jlehr Jan 03 '12 at 14:23
  • 1
    @Abizern `@interface NSObject (MyCat) { int myivar; } @end` -> Ivars may not be placed in categories – iHunter Jan 03 '12 at 14:28

2 Answers2

9

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.

iHunter
  • 6,205
  • 3
  • 38
  • 56
  • Heh, another good reason not to leave `NSZombie` enabled by default. – jlehr Jan 03 '12 at 13:58
  • @jlehr And what are the other reasons? I always enable them in my projects, and haven't seen any drawbacks so far. Am I missing something? Thank you. – iHunter Jan 03 '12 at 14:07
  • 1
    Leaving zombies enabled can mask a variety of problems, and of course it causes your app to leak tons of memory. – jlehr Jan 03 '12 at 14:13
  • @jlehr Are you sure? Without zombies you cannot reliably find bugs with access to deallocated object until its wiped by another object, and a crash occurs. But you may be unlucky to not a crash, but call the absolutely unrelated object, so it's really more dangerous to live without zombies IMO). Yes, the memory can be a problem, but I haven't seen any real issues yet, seems they use some kind of memory saving or I'm just lucky enough :) – iHunter Jan 03 '12 at 14:54
  • 1
    There's no reason to leave zombies enabled unless you're actively debugging memory management issues. – jlehr Jan 03 '12 at 17:09
  • 1
    I noticed this when running the Leaks tool. I seem to recall the overridden dealloc was not even called on the object itself, but the ARC properties were getting zeroed out as if dealloc was called. Scary. +1 for having zombies enabled during the development/debug cycle by the way. – Steven Kramer Jan 12 '12 at 12:23
  • @iHunter In certain circumstances, NSZombies can silently allow crashing code to run. – Nate Chandler Aug 16 '12 at 16:08
1

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?

jlehr
  • 15,557
  • 5
  • 43
  • 45