Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

RagingGoat

macrumors 6502
Original poster
Jun 21, 2010
307
15
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];
    }
    
}
 

Punicasoft

macrumors member
Jul 27, 2013
37
0
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];
    }
    
}
 

RagingGoat

macrumors 6502
Original poster
Jun 21, 2010
307
15
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)


I am using ARC.
 

RagingGoat

macrumors 6502
Original poster
Jun 21, 2010
307
15
Then why are you autoreleasing here?:
Code:
TUSafariActivity *activity = [[[TUSafariActivity alloc] init] autorelease];

I'm not. He added that to the code snippet when he suggested it.
 

Punicasoft

macrumors member
Jul 27, 2013
37
0
I am using ARC.

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;
    }
 

RagingGoat

macrumors 6502
Original poster
Jun 21, 2010
307
15
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;
    }

Nope. Leak is still there.
 

Punicasoft

macrumors member
Jul 27, 2013
37
0
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;

}
 

RagingGoat

macrumors 6502
Original poster
Jun 21, 2010
307
15
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.
 

Punicasoft

macrumors member
Jul 27, 2013
37
0
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.

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];
}
 

RagingGoat

macrumors 6502
Original poster
Jun 21, 2010
307
15
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
 

ElectricSheep

macrumors 6502
Feb 18, 2004
498
4
Wilmington, DE
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

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.
 

Duncan C

macrumors 6502a
Jan 21, 2008
853
0
Northern Virginia
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.


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.)
 
Last edited:

MattInOz

macrumors 68030
Jan 19, 2006
2,760
0
Sydney
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?
 
Last edited:

Punicasoft

macrumors member
Jul 27, 2013
37
0
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

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

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.)

activityVC is NOT strongly attached to any property in self, so there is no retain cycle

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?

Tapping on the bar button did trigger the event.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.