iOS 5 Twitter框架& completionHandler block - "捕捉' self'在这个区块中强烈地可能导致保留周期"

时间:2011-11-16 21:54:02

标签: iphone objective-c ipad ios5 objective-c-blocks

我是编程和Objective-C的新手,我正在尝试弄清楚我的代码有什么问题。我已经阅读了一些关于块的内容,但我不知道到目前为止我所阅读的内容与我的代码有什么关系。

我的代码正在使用iOS 5 Twitter框架。我使用了Apple提供的大部分示例代码,所以我实际上一开始并不知道我使用了一个块作为完成处理程序。

现在我从Xcode 4获得这两条消息,说“ 1。块将由被捕获对象强烈保留的对象保留”和“在此块中强烈捕获'self'可能导致保留周期“。

基本上,我所做的是删除Apple在其完成处理程序中使用的代码(使用TWTweetComposeViewControllerResultCancelled和TWTweetComposeViewControllerResultDone的switch语句)并使用我的if语句[imagePickerController sourceType]

因此在将图像添加到推文后调用sendTweet

我希望有人可以向我解释为什么会这样,以及我如何解决它。另外:我可以将完成处理程序代码放入方法而不是块吗?

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

4 个答案:

答案 0 :(得分:21)

基本问题是您在一个区块内使用self。该对象保留该块,块本身也保留该对象。所以你有一个保留周期,因此两者都可能永远不会被释放,因为它们都有一个指向它们的引用。 Fortunaly有一个简单的解决方法:

通过对自身使用所谓的弱引用,块将不再保留对象。然后可以释放该对象,该对象将释放块(将MyClass设置为适当的类型):

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

您可以使用weakSelf代替self。请注意,这仅适用于使用ARC的iOS 5。

在此看来,How do I avoid capturing self in blocks when implementing an API?还有一个非常好的长解释,它也描述了如何在iOS 4和没有ARC的情况下避免这种情况。

答案 1 :(得分:3)

您的块正在保留self,因为您使用self作为UIAlertView的委托。如果self也保留了块,则它们会相互保留,从而形成保留周期。

尝试

 __block WhateverTypeSelfIs *nonRetainedSelfForBlock = self;
[tweetViewController setCompletionHandler: 

UIAlertView *alert = [[UIAlertView alloc] 
                                 initWithTitle:alertTitle 
                                 message:alertMessage 
                                 delegate:nonRetainedSelfForBlock 
                                 cancelButtonTitle:alertCancelButtonTitle 
                                 otherButtonTitles:otherAlertButtonTitle,nil];

检查Apple docs对象和块变量部分。除非使用__block,否则将保留块中引用的对象。

答案 2 :(得分:1)

根据我在其他地方看到的内容,“ weak”不适用于符合ARC的代码,而且必须使用“ _unsafe_unretained”。这就是我在Apple示例应用程序“AVPlayerDemo”中修复“捕获'自我'在这个块中强烈可能导致保留周期”的警告:

__unsafe_unretained id unself = self;
mTimeObserver = [mPlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC) 
                            queue:NULL /* If you pass NULL, the main queue is used. */
                            usingBlock:^(CMTime time) 
                                        {
                                            /* 'unself' replaced 'self' here: */
                                            [unself syncScrubber];
                                        }];

答案 3 :(得分:0)

有一种相当令人惊讶的方法可以摆脱警告。请注意,只有在确定自己没有创建保留周期时才这样做,或者您确定稍后会自行解除(例如,在完成后将完成处理程序设置为nil)。如果您可以控制界面,则重命名您的方法,使其不以“set”开头。仅当方法名称以“set”开头时,编译器似乎才会发出此警告。