38

I have a UIViewController that is pushed onto a container controller and then popped off, and using the allocations instrument, I can see that the view controller is destroyed afterwards. However, a breakpoint in the controller's dealloc is never reached. Does anyone know why dealloc isn't called? Is it possible for ARC to destroy an object without calling dealloc?

Also, I've disabled NSZombies (some have said that can cause dealloc not to fire).

Edit:

Dealloc doesn't do much, just prints to the console, and it never gets called:

- (void)dealloc { NSLog(@"Deallocating..."); }

I can't post the container controller–it's proprietary and too complicated. Dealloc is called consistently on some controllers and not others. If I can find the time I will try and post a simplified version that reproduces the problem.

Is there any way to verify that NSZombies is disabled?

Edit2

I'm posting a screenshot from instruments; it looks to me like it's properly deallocating.

enter image description here

rob
  • 4,069
  • 3
  • 34
  • 41
  • 1
    It should be getting called, even under ARC. You cannot include [super dealloc] though, ARC will add that for you. Perhaps share the code for this class? – picciano Feb 09 '12 at 21:59
  • Are there any methods in the dealloc method? – zaph Feb 09 '12 at 22:04
  • 1
    You should post the `-(void)dealloc{}` code. – NJones Feb 09 '12 at 22:05
  • Did you try using `NSLog(...)` to mark your `dealloc()` instead of breakpoints? – John Estropia Feb 10 '12 at 02:08
  • If you're using ARC, what code did you put in your dealloc? Perhaps if the dealloc code doesn't do anything, the compiler is just optimising it away. Did you try putting an NSLog() in there instead of a breakpoint? – Nick Lockwood Feb 10 '12 at 15:51
  • By using Instruments to verify that the view controller is being destroyed, are you using allocations and its heap shot functionality to take heap shots every time you repeatedly push and pop this controller? You shouldn't see any accumulation of this view controller on each pass through this push / pop cycle. Also, does the Leaks instrument show any retain cycles? As others have stated, `-dealloc` should be getting called here. – Brad Larson Feb 10 '12 at 18:12
  • I'm having a similar problem. My dealloc isn't being called, and I need it to since I'm calling a CFRelease on CTFrameRef... (did you ever find the solution?) – Joseph Humfrey Apr 11 '12 at 11:20
  • I never found a solution...Leaks could not find any retain cycle, instruments would verify a release, but dealloc would not be called. – rob Apr 12 '12 at 03:51
  • Echoing erurainon did you ever try an NSLog in dealloc and running without instruments or zombies and seeing if it gets logged ? Two other ways zombies could be turned on is in your schemes under diagnostics or if you set the requisite variables in your bash_profile at some point in the past. – Colin Dec 04 '12 at 22:00
  • Guys, we don't have to leave a function scope empty `deinit { print("deinit") }` – kokos8998 Jul 04 '16 at 22:35
  • I do three things that find 9/10 memory leaks in my view controllers. 1) Search for `^` to find blocks and see if any of those blocks have references to `self` in them, and see if any classes retain those blocks somewhere. 2) Search for `= self` to see if I'm setting self as a delegate or some other property elsewhere where the reference is a strong one. 3) If I'm using an NSTimer I'm likely not invalidating it... – Mike Oct 06 '16 at 17:15

7 Answers7

93

I just ran across a similar issue. Do you have any blocks where you are referring to 'self'? I had some blocks for notification observation in my init where I was referring to 'self'. Under ARC self is retained in blocks. My dealloc wasn't getting called and that was where I was removing the observation.

The trick is to create a __weak (iOS 5+) or __unsafe_unretained (iOS 4.x) reference to your 'self' and use that to access self or any _iVars (these will cause 'self' to be retained as well) in the block. Here's an example.

__unsafe_unretained TableViewController *weakSelf = self;
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
        if (weakSelf.tableView) {
            [weakSelf.tableView reloadData];
        }
    }];
mikeq
  • 941
  • 6
  • 3
  • After code reviewing lots of migrated code (from non-arc projects to arc projects), I can confirm that this is a major source of undesired retain-cycles. In the end: a block comes in handy and is easily created, right! – leviathan Jun 13 '13 at 09:48
  • By not unregistering the observer, that means it will fire in response to the notification *forever*, even after `self` is long gone. And if you add this observer multiple times, then each time this notification happens, it will fire multiple useless observers, forever. This cannot be good programming practice. – user102008 Dec 31 '13 at 01:41
  • 7
    For simplicity we can use `__weak typeof(self) weakSelf = self;` – Nat Feb 24 '14 at 11:51
  • `[NSNotificationCenter addObserverForName:object:queue:usingBlock:]` returns an opaque object to act as the observer. You need to unsubscribe. – k06a Jan 16 '15 at 11:18
25

In my case it was NSTimer. It retains its target so you need to invalidate timer when you're done with view controller.

shim
  • 9,289
  • 12
  • 69
  • 108
Almas Sapargali
  • 451
  • 4
  • 9
21

If dealloc is not being called by the VC, then I would bet there is a circular reference somewhere in your code, which is preventing ARC from calling dealloc.

Some things to check:

  1. Do you have an object instantiated that references back to the VC?
  2. If you need to reference the VC be sure you have used '__unsafe_unretained' attribute or 'weak' (iOS5+) so that the retain cycle does not occur.

I was nipped in the butt when my delegate declarations did not utilize __unsafe_unretained.

Dean Liu
  • 501
  • 5
  • 12
16

Even with ARC you can inspect the reference count manually:

CFIndex rc = CFGetRetainCount((__bridge CFTypeRef)myObj);

You can know for sure if your code is hung in a memory cycle.

jbbenni
  • 1,158
  • 12
  • 31
  • I agree with @Dean Liu - Instruments not withstanding, I suspect it isn't deallocating due to a memory cycle. I occasionally get snagged with cycles using Blocks - especially if the block references 'self', it is very easy to do. – jbbenni Apr 22 '13 at 19:50
  • 3
    This helped! (It sure would be nice if there was a corresponding function call method that gave a list of all the objects that referenced an object). – Chris Prince Sep 04 '14 at 19:33
15

My problem was delegates.

Check your delegates! The delegate property should have weak specified:

weak var delegate: SomeProtocol?

or

@property (weak, nonatomic) id<SomeProtocol> delegate;

kgaidis
  • 14,259
  • 4
  • 79
  • 93
10

Here's another tip (happened to me):

tl;dr: Look at your class instance variables as well, not just the class properties. Are you allocating any instance variable in code, and not setting it to nil later?

I had a class with a bunch of properties (@property (nonatomic, strong) and @property (nonatomic, weak)) in its header file. I commented out those properties one by one to see whether this will change anything. It did not. The main class was still not being deallocated. So the problem was not in the properties.

What I did afterwards was look at the instance variables. I had one instance variable (which was a UIViewController), which I created on viewDidLoad. This one never got dealloc-ed!

When I removed this variable, sure enough, my main class called dealloc.

So because this one wasn't dealloc-ed, my main class was not dealloc-ed either. Moving that instance variable as a property solved the problem for me.

Question: I'm not sure why this happens though. Does the operating system have better control over the properties of a class, than over the instance variables? It seems a bit weird that the parent class which contains the instance variable doesn't get released because of its instance variable.

Alex
  • 7,432
  • 20
  • 75
  • 118
5

Do you have NSZombieEnabled? See this question:

Why is object not dealloc'ed when using ARC + NSZombieEnabled

I was stumped by this for a while myself...

Community
  • 1
  • 1
TomSwift
  • 39,369
  • 12
  • 121
  • 149