Memory Leak

Discussion in 'iOS Programming' started by RagingGoat, Aug 1, 2013.

  1. RagingGoat macrumors 6502

    Joined:
    Jun 21, 2010
    #1
    I am showing a memory leak with my excludedActivityTypes array in my iPad app and I can't figure out how to fix it. Can anyone help me out?

    Code:
    - (void)showMenu
    {
        NSURL *urlToShare = hackyURL;
        NSArray *activityItems = @[urlToShare];
        TUSafariActivity *activity = [[TUSafariActivity alloc] init];
        
        UIActivityViewController *activityVC = [[UIActivityViewController alloc]initWithActivityItems:activityItems applicationActivities:@[activity]];
        activityVC.excludedActivityTypes = @[UIActivityTypeAssignToContact, UIActivityTypePostToWeibo, UIActivityTypeSaveToCameraRoll];
        
        if ([self.popover isPopoverVisible])
        {
            [self.popover dismissPopoverAnimated:YES];
            self.popover = nil;
        }
        else
        {
            self.popover = [[UIPopoverController alloc]initWithContentViewController:activityVC];
            [self.popover presentPopoverFromBarButtonItem:systemAction permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
        }
        
    }
    
    
     
  2. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #2
    You need the "autorelease" after "alloc] init]" to balance the reference count.
    Depending on how your .popover property is declared, you may or may not need the autorelease.
    (you need the autorelease if it's a strong reference)

    Code:
    - (void)showMenu
    {
        NSURL *urlToShare = hackyURL;
        NSArray *activityItems = @[urlToShare];
        TUSafariActivity *activity = [[[TUSafariActivity alloc] init] autorelease];
        
        UIActivityViewController *activityVC = [[[UIActivityViewController alloc]initWithActivityItems:activityItems applicationActivities:@[activity]] autorelease];
        activityVC.excludedActivityTypes = @[UIActivityTypeAssignToContact, UIActivityTypePostToWeibo, UIActivityTypeSaveToCameraRoll];
        
        if ([self.popover isPopoverVisible])
        {
            [self.popover dismissPopoverAnimated:YES];
            self.popover = nil;
        }
        else
        {
            self.popover = [[[UIPopoverController alloc]initWithContentViewController:activityVC] autorelease];
            [self.popover presentPopoverFromBarButtonItem:systemAction permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
        }
        
    }
     
  3. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #3

    I am using ARC.
     
  4. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #4
    Then why are you autoreleasing here?:
    Code:
    TUSafariActivity *activity = [[[TUSafariActivity alloc] init] autorelease];
     
  5. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #5
    I'm not. He added that to the code snippet when he suggested it.
     
  6. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #6
    Oops. My apologies.
     
  7. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
  8. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #8
    My Bad, you see I am not that into ARC (yet).

    Another guess, since the excludedActivityTypes is a copy, I boldly assume it's their leak, not yours.

    Code:
    @property(nonatomic,copy) NSArray *excludedActivityTypes;
    Maybe trying this can help forcing it to release?

    Code:
        if ([self.popover isPopoverVisible])
        {
            [self.popover dismissPopoverAnimated:YES];
            UIActivityViewController *ac = (id)popover.contentViewController;
            ac.excludedActivityTypes = nil;
            self.popover = nil;
        }
    
     
  9. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #9
    Nope. Leak is still there.
     
  10. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #10
    Um... try #3, what about replacing
    Code:
        if ([self.popover isPopoverVisible])
        {
            // I am thinking maybe this path never get called?
            [self.popover dismissPopoverAnimated:YES];
            self.popover = nil;
        }
        else
        {
            self.popover = [[UIPopoverController alloc]initWithContentViewController:activityVC];
            [self.popover presentPopoverFromBarButtonItem:systemAction permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
        }
    
    with such

    Code:
    
            UIPopoverController *po = [[UIPopoverController alloc]initWithContentViewController:activityVC];
            po.delegate = self;
            [po presentPopoverFromBarButtonItem:systemAction permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    
    
    and add

    Code:
    
    - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
        
        UIActivityViewController *ac = (id)popoverController.contentViewController;
        ac.excludedActivityTypes = nil;
    
    }
    
    
     
  11. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #11
    It is coded the way it is because I want the user to be able to dismiss the popover by tapping the button again or by tapping an area outside of the popover. If I use the code you have, the app will crash if the user tries to dismiss the popover by tapping the button.
     
  12. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #12
    I hate memleaks as much as you do. And the following code should work unless my simulator is lying.
    The code is in non-ARC and tested in non-ARC.

    Code:
    - (void)poShowHideButton:(UIBarButtonItem *)item {
    
        if (self.popover) {
            if ([self.popover isPopoverVisible]) {
                [self.popover dismissPopoverAnimated:YES];
            }
            return;
        }
    
        NSArray *activityItems = @[[NSURL URLWithString:@"http://www.test.com"]];
        
        UIActivityViewController *activityVC = [[[UIActivityViewController alloc]initWithActivityItems:activityItems applicationActivities:nil] autorelease];
    
        activityVC.excludedActivityTypes = @[UIActivityTypeAssignToContact, UIActivityTypePostToWeibo, UIActivityTypeSaveToCameraRoll];;
        
        activityVC.completionHandler = ^(NSString *service, BOOL completed) {
            activityVC.excludedActivityTypes = nil;
            self.popover = nil;
        };
        self.popover = [[[UIPopoverController alloc]initWithContentViewController:activityVC] autorelease];
        [self.popover presentPopoverFromBarButtonItem:self.navigationItem.rightBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }
     
  13. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #13
    Thanks! That seems to have gotten rid of the leak. I do get a warning on this line:
    Code:
    activityVC.excludedActivityTypes = nil;
    
    The warning is:
    Capturing 'activityVC' strongly in this block is likely to lead to a retain cycle
     
  14. ElectricSheep macrumors 6502

    ElectricSheep

    Joined:
    Feb 18, 2004
    Location:
    Wilmington, DE
    #14
    This is because by using 'activityVC' in the block, it now has a strong reference to it. If all other references are removed, activityVC will not be released until this block executes; the block will never execute because there are no longer any other references to activityVC.

    The way around this is to create a weak reference to activityVC, and capture that in the block:

    Code:
        UIActivityViewController * __weak weakActivityVC = activityVC;
    
        activityVC.completionHandler = ^(NSString *service, BOOL completed) {
            weakActivityVC.excludedActivityTypes = nil;
            self.popover = nil;
        };
    
    This should remove the retain cycle warning.
     
  15. Duncan C, Aug 2, 2013
    Last edited: Aug 2, 2013

    Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #15

    Although the reference to self is also likely to cause a retain cycle, so the OP probably also needs to fix that:

    Code:
        UIActivityViewController * __weak weakActivityVC = activityVC;
        MyVCClass *  __weak myself = self;
    
        activityVC.completionHandler = ^(NSString *service, BOOL completed) {
            weakActivityVC.excludedActivityTypes = nil;
           myself.popover = nil;
        };
    
    (where you would replace "MyVCClass" with the class name of the VC that is running this code.)
     
  16. MattInOz, Aug 2, 2013
    Last edited: Aug 2, 2013

    MattInOz macrumors 68030

    MattInOz

    Joined:
    Jan 19, 2006
    Location:
    Sydney
    #16
    As I understand what your saying you want the user to be able tap the same button that the popover presents from again to dismiss the popover. But that button is already outside the popover frame and any tap outside the frame dismisses the popover and cancels the touch event.

    So how will your button ever receive touch event to fire the problem code?
     
  17. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #17
    I am not sure why this warning didn't pop in non-ARC code.
    Despite the warning, there is no resource leaks,
    since the completionHandler is say to be "set to nil after call" in the UIActivityViewController.h file,
    thus breaking the retain loop.

    Code:
    @property(nonatomic,copy) UIActivityViewControllerCompletionHandler completionHandler;  // set to nil after call
    
    activityVC is NOT strongly attached to any property in self, so there is no retain cycle

    Tapping on the bar button did trigger the event.
     

Share This Page