Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy does my view controller (presented modally from a storyboard segue) not get released after being dismissed?
    primarykey
    data
    text
    <p>My view controller (code below) is presented modally from a storyboard segue (attached to a UIButton). It is then dismissed once one of the (dynamically generated) buttons is tapped. For some reason, it is not subsequently released (has a retain count of 1).</p> <p>Clearly the first concern would be the two objects (PPAPI and PPObjectCache) which hold this object as a delegate (registered in viewDidLoad), however these both use weak references which will automatically be NULLed by ARC if I can get whatever else is retaining it to release it. I have verified that these objects are not holding a strong reference to this view controller.</p> <p>I have used Instruments (the Allocations profile) to check the retains/releases of this object, and the report is shown below the code. As you can see, the imbalance is in UIKit or Foundation code, but it’s hard for me to see where exactly, or why.</p> <p>Can anyone spot the reason why UIKit or Foundation is holding on to my view controller?</p> <pre><code>#import "PPLoginViewController.h" #import "PPAppDelegate.h" @interface PPLoginViewController () { NSArray *employeeIDs; } @end @implementation PPLoginViewController - (void)viewDidLoad { [super viewDidLoad]; PPAPI *api = [PPAPI api]; [api subscribeToViewsOfType:@"employees" delegate:self]; [[PPObjectCache cache] subscribeToChangesToObjectsOfType:@"worker" delegate:self]; [api callMethod:@"main.tasks.get_employees" withParameters:@{} callback:nil]; } // PPAPI view subscription delegate method - (void)receivedObjectIDs:(NSArray *)objectIDs forViewType:(NSString *)viewType { NSLog(@"%@: GOT VIEW", self); dispatch_async(dispatch_get_main_queue(), ^{ employeeIDs = objectIDs; [self reload]; }); } // PPObjectCache delegate method - (void)objectChanged:(id)object { dispatch_async(dispatch_get_main_queue(), ^{ [self reload]; }); } - (void)reload { [[self.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { return [evaluatedObject isKindOfClass:[UIButton class]]; }]] makeObjectsPerformSelector:@selector(removeFromSuperview)]; UIDevice *currentDevice = [UIDevice currentDevice]; CGFloat y = 75.f; for (NSNumber *employeeID in employeeIDs) { NSDictionary *employee = [[PPObjectCache cache] objectOfType:@"worker" withID:employeeID]; UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:employee[@"name"] forState:UIControlStateNormal]; button.titleLabel.font = [UIFont boldSystemFontOfSize:18.f]; button.tag = [employeeID integerValue]; button.frame = CGRectMake((self.view.bounds.size.width - 240.f) / 2.f, y, 240.f, currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad ? 60.f : 45.f); [button addTarget:self action:@selector(login:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; y += currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad ? 75.f : 65.f; } } // UIButton action from above - (void)login:(UIButton *)sender { PPAppDelegate *delegate = (PPAppDelegate *)[UIApplication sharedApplication].delegate; delegate.loggedInEmployee = [[PPObjectCache cache] objectOfType:@"worker" withID:@(sender.tag)]; // self.logoutButton is an @property (weak) set from the presenting view controller self.logoutButton.title = [NSString stringWithFormat:@"Logout %@", delegate.loggedInEmployee[@"initials"]]; [self dismissViewControllerAnimated:YES completion:nil]; } @end </code></pre> <p>Allocations report follows (sorry for poor formatting). As you can see, the events for which my application code is responsible are all balanced (show as Retain/Release groups or are immediately followed by balancing calls). Something in UIKit or Foundation isn’t releasing my view controller!</p> <pre><code># Event Type ∆ RefCt RefCt Timestamp Responsible Library Responsible Caller 0 Malloc +1 1 00:19.062.502 UIKit -[UIClassSwapper initWithCoder:] 1 Retain +1 2 00:19.062.991 UIKit -[UIRuntimeConnection initWithCoder:] 2 Retain +1 3 00:19.063.036 UIKit -[UIRuntimeConnection initWithCoder:] 3 Retain +1 4 00:19.063.230 UIKit UINibDecoderDecodeObjectForValue 4 Retain +1 5 00:19.063.274 UIKit UINibDecoderDecodeObjectForValue 5 Retain +1 6 00:19.063.315 Foundation -[NSObject(NSKeyValueCoding) setValue:forKey:] 6 Retain +1 7 00:19.063.388 UIKit -[UINib instantiateWithOwner:options:] Release (2) -2 00:19.063.568 UIKit -[UINibDecoder finishDecoding] 8 Release -1 5 00:19.063.599 UIKit -[UINibDecoder finishDecoding] 10 Release -1 3 00:19.063.722 UIKit -[UIRuntimeConnection dealloc] 11 Release -1 2 00:19.063.755 UIKit -[UIRuntimeConnection dealloc] 12 Retain +1 3 00:19.063.920 UIKit -[UIStoryboardSegue initWithIdentifier:source:destination:] Retain/Release (2) 00:19.067.387 Purchase -[PPMasterViewController prepareForSegue:sender:] 15 Retain +1 4 00:19.072.460 UIKit -[UIViewController setChildModalViewController:] 16 Retain +1 5 00:19.076.305 UIKit -[UINib instantiateWithOwner:options:] 17 Retain +1 6 00:19.076.366 UIKit -[UINib instantiateWithOwner:options:] 18 Retain +1 7 00:19.076.582 UIKit -[UIProxyObject initWithCoder:] 19 Retain +1 8 00:19.076.587 UIKit -[UIRuntimeConnection initWithCoder:] 20 Retain +1 9 00:19.080.167 UIKit UINibDecoderDecodeObjectForValue 21 Retain +1 10 00:19.080.230 UIKit UINibDecoderDecodeObjectForValue 22 Release -1 9 00:19.080.449 UIKit -[UINib instantiateWithOwner:options:] 23 Release -1 8 00:19.080.507 UIKit -[UINib instantiateWithOwner:options:] 24 Release -1 7 00:19.080.578 UIKit -[UINibDecoder finishDecoding] Release (2) -2 00:19.080.602 UIKit -[UINibDecoder finishDecoding] 26 Release -1 5 00:19.080.716 UIKit -[UIRuntimeConnection dealloc] Retain/Release (2) 00:19.081.788 Purchase -[PPAPI subscribeToViewsOfType:delegate:] Retain/Release (2) 00:19.081.866 Purchase -[PPObjectCache subscribeToChangesToObjectsOfType:delegate:] 32 Retain +1 5 00:19.089.975 UIKit -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] 33 Retain +1 6 00:19.091.352 UIKit __91-[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:]_block_invoke_0238 Retain/Release (2) 00:19.105.379 UIKit -[UIResponder becomeFirstResponder] 36 Retain +1 7 00:19.106.122 UIKit -[UIViewController presentViewController:withTransition:completion:] 37 Retain +1 8 00:19.106.142 UIKit -[UIViewController presentViewController:withTransition:completion:] 38 Release -1 7 00:19.108.717 UIKit _UIApplicationHandleEvent 39 Release -1 6 00:19.109.517 UIKit -[UIStoryboardSegue dealloc] 40 Release -1 5 00:19.109.534 UIKit _UIApplicationHandleEvent 41 Release -1 4 00:19.109.581 UIKit -[UIStoryboardScene dealloc] Retain/Autorelease/Release (5) +1 00:19.145.293 Foundation -[NSConcreteHashTable countByEnumeratingWithState:objects:count:] Retain (2) +2 00:19.151.847 Purchase -[PPLoginViewController receivedObjectIDs:forViewType:] Retain (2) +2 00:19.151.874 Purchase __copy_helper_block_ Release (2) -2 00:19.151.888 Purchase -[PPLoginViewController receivedObjectIDs:forViewType:] Release (2) -2 00:19.278.813 Purchase __destroy_helper_block_ 48 Release -1 4 00:19.541.189 UIKit -[UIWindowController transitionViewDidComplete:fromView:toView:removeFromView:] Retain/Release (2) 00:19.996.260 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:] 51 Release -1 3 00:19.996.269 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:] 52 Release -1 2 00:19.996.302 UIKit -[UIPeripheralHost(UIKitInternal) _stopPinningInputViewsOnBehalfOfResponder:] 53 Retain +1 3 00:19.996.776 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:] 54 Retain +1 4 00:20.001.379 UIKit __91-[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:]_block_invoke_0238 55 Release -1 3 00:20.002.196 UIKit -[UIViewController _dismissViewControllerWithTransition:from:completion:] 56 Retain +1 4 00:20.432.653 UIKit -[UIViewController _didFinishDismissTransition] 57 Release -1 3 00:20.432.658 UIKit -[UIViewController setChildModalViewController:] 58 Release -1 2 00:20.432.662 UIKit -[UIViewController _didFinishDismissTransition] 59 Release -1 1 00:20.432.706 UIKit -[UIWindowController transitionViewDidComplete:fromView:toView:removeFromView:] Retain/Autorelease (2) +1 00:20.663.794 Foundation hashProbe </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. COTry using __block references to self in your blocks. __block PPLoginViewController *bself = self;
      singulars
    2. CONo change adding the above line of code before each block and altering the block to reference `bself` instead of `self`. Thanks for the idea. I think if something like that had been causing the unbalanced retain (which I can’t see a way it could), it would show in the above Allocations report. The block retains are shown, but are balanced by releases.
      singulars
    3. COYes, `dealloc` is not being called due to something within Foundation or UIKit retaining my view controller (as shown by the Allocations report). I’ve tried removing all blocks from the code (using `[self performSelectorOnMainThread:@selector(reload) withObject:nil waitUntilDone:NO]` instead) and the problem persists. Adding the weak self / strong self dance while still using blocks makes no difference. There are no blocks being stored anyway (GCD executes and then releases them) so retaining self in the block will not cause a cycle. See http://stackoverflow.com/questions/7565953
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload