7

I have spent some time debugging a weird issue with ARC and custom dealloc functions.

  1. I'm subclassing NSOperation class
  2. I set completion block for this operation
  3. The operation is referenced by a strong property of very flat object (no methods, automatic ivars, two strong properties) lets call this object DataRequest
  4. following all guidelines the completion block uses only weak references to local objects (including the operation itself)
  5. neither compiler nor analyzer generate any issues
  6. DataRequest holds the ONLY reference to the operation I generate and is destroyed in the operation completion block. It's ALWAYS destroyed (its dealloc always executed)
  7. My operation has a custom dealloc. I have only a single NSLog call in it.

... and the issue is:

If I run this under in debugger, the breakpoint in the dealloc is never hit, the log message never appears. Primarily I thought the operation was leaking.

If I run this in instruments, all is fine, the system console prints the message and Allocations instrument reports the operation being freed from the proper stack snapshot including the custom dealloc. No leaks detected.

I'm 100% sure I use the same compiler settings for debugging and for profiling.

The most confusing thing at the end: If I create a custom version of [DataRequest dealloc] and I put self.operation = nil; to it - all works fine even from the debugger.

Does anybody have some hints what compiler linker options to try to see some difference? can this be bug in Apple tools (all of us were in the position blaming a big fish for our own errors, right?)

... and yes I have tried with GDB and LLDB. Result was the same - what might indicate something.

I have tried to create a minimalistic sample but it just worked (indeed) ;)

Thanks

Phlibbo
  • 5,073
  • 2
  • 36
  • 52
simpleone
  • 290
  • 2
  • 10
  • I have done a very basic observation... if I run the app on the simulator from XCode (GDB or LLDB) my log messages in `dealloc` are not printed. If I just quit the debugger and launch the app straight from the simulator - the Console.app show all messages. No compile no linking in between. Strange. – simpleone Oct 13 '11 at 12:13
  • ... and one even more straightforward result... If I run the app from XCode in a debugging session - dealloc is not called (log not printed) if I run the app manually in the simulator and then attach the debugger... all is as expected. – simpleone Oct 13 '11 at 12:54

3 Answers3

7

Do you have NSZombiesEnabled? We had the same issue and "solved" it by disabling NSZombies.

"Product" -> "Scheme" -> "Edit scheme" -> "Diagnostics" -> Uncheck "Enable Zombie Objects"

I'm not sure why dealloc isn't called when NSZombies are enabled (I'm pretty sure it was called before ARC).

the_critic
  • 12,014
  • 19
  • 60
  • 109
Ray Lillywhite
  • 730
  • 4
  • 12
  • I have to test - but yes, I have NSZombiesEnabled set. This sounds quite likely - and at least it gives some reasonable explanation for different behavior. – simpleone Oct 31 '11 at 19:08
  • 1
    I can confirm NSZombiesEnabled is causing the issue. Many thanks! – simpleone Oct 31 '11 at 19:18
  • 1
    This can happen all over the place when using NSZombiesEnabled with ARC, not just with `NSOperation`. The key thing if observing dealloc weirdness with ARC is to disable NSZombiesEnabled (which is labelled "Enabled Zombie Objects" in the Edit Schemes... > Diagnostics tab in Xcode 4.) – Jonathan Ellis Dec 14 '11 at 14:32
  • 1
    The reason dealloc isn't called is that the object is never deallocated. It is prevented from doing so and instead is turned into a "zombie" – borrrden Oct 24 '12 at 01:28
  • @borrrden I think it could be deallocated and just replaced by a zombie object at the same address. AFAIK, that's how it worked pre-arc. – Ray Lillywhite Oct 31 '12 at 23:26
  • @RayLillywhite You may be right. I dont remember if dealloc was called or not now that I think about it. I just remember that the memory was not actually freed and it showed badly in Instruments. – borrrden Nov 01 '12 at 00:27
1

I ran into the same kind of issue today and I took me about 5 hours to find that the problem was caused by NSZombies enabled in my project settings.

I also agree that before ARC, dealloc was called in this case.

After many tests it appears that dealloc is not called if using iOS 5.x (device or simulator).

but it gets called again (with Zombies enabled) in iOS 6.x (device or simulator)

I don't know if this change is caused by a bug in ios5 that has been patched in ios6, or a feature introduced and rolled back.

Hope that helps...

poloDelaVega
  • 1,145
  • 1
  • 9
  • 5
1

I ran into the same kind of issue today but my problem was a retain cycle generated by a block.

If you're using blocks:

  1. Make sure SELF doesn't appear inside the block.
  2. If you need to use SELF inside a block, use a weak reference.
  3. Make sure there are no macros inside the block that may be referencing self (like NSAssert).