Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 5 Twitter Framework & completionHandler block - "Capturing 'self' strongly in this block is likely to lead to a retain cycle"

I am very new to programming and Objective-C and I am trying to work out what is wrong with my code. I have read a bit about blocks but I don't know how any of what I have read so far is relevant to my code.

My code is using the iOS 5 Twitter Framework. I use most of the sample code that Apple provides so I actually had no clue at first that I was using a block for the completion handler.

Now I get those two messages from Xcode 4 saying "1. Block will be retained by an object strongly retained by the captured object" and "Capturing 'self' strongly in this block is likely to lead to a retain cycle".

Basically, what I did is to remove the code Apple used in their completion handler (switch statement with TWTweetComposeViewControllerResultCancelled & TWTweetComposeViewControllerResultDone) and used my if statements with [imagePickerController sourceType].

So the sendTweet gets called after an image has been added to the tweet.

I hope someone can explain to me why this is happening and how I can solve it. Also: can I put the completion handler code into a method instead of a block?

- (void)sendTweet:(UIImage *)image
{
    //adds photo to tweet
    [tweetViewController addImage:image];

    // Create the completion handler block.
    //Xcode: "1. Block will be retained by an object strongly retained by the captured object"
    [tweetViewController setCompletionHandler:
                             ^(TWTweetComposeViewControllerResult result) {
            NSString *alertTitle,
                     *alertMessage,
                     *otherAlertButtonTitle,
                     *alertCancelButtonTitle;

            if (result == TWTweetComposeViewControllerResultCancelled) 
            {
                //Xcode: "Capturing 'self' strongly in this block is likely to lead to a retain cycle"
                if ([imagePickerController sourceType])
                {
                    alertTitle = NSLocalizedString(@"TCA_TITLE", nil);
                    alertMessage = NSLocalizedString(@"TCA_MESSAGE", nil);
                    alertCancelButtonTitle = NSLocalizedString(@"NO", nil);
                    otherAlertButtonTitle = NSLocalizedString(@"YES", nil);

                    //user taps YES
                    UIAlertView *alert = [[UIAlertView alloc] 
                                             initWithTitle:alertTitle 
                                                   message:alertMessage 
                                                  delegate:self   // Note: self
                                         cancelButtonTitle:alertCancelButtonTitle 
                                         otherButtonTitles:otherAlertButtonTitle,nil];
                    alert.tag = 1;
                    [alert show];                
                }            
            }
like image 916
iMaddin Avatar asked Nov 16 '11 21:11

iMaddin


1 Answers

The basic problem is that you're using self within a block. The block is being retained by the object and the block itself retains the object, too. So you have a retain-cycle and thus both will probably never be released because both have a reference pointing towards them. Fortunaly there is a simple workaround:

By using a so-called weak reference to self the block will no longer retain the object. The object then can later be released which will release the block (set MyClass to the appropriate type):

// before your block
__weak MyObject *weakSelf = self;

Inside your block you now can use weakSelf instead of self. Note that this is only for iOS 5 using ARC.

Looking at this there is also a very good long explanation on How do I avoid capturing self in blocks when implementing an API? which also describes how to avoid this on iOS 4 and without ARC.

like image 87
Dennis Bliefernicht Avatar answered Oct 11 '22 11:10

Dennis Bliefernicht