【问题标题】:iOS 5 Twitter Framework & completionHandler block - "Capturing 'self' strongly in this block is likely to lead to a retain cycle"iOS 5 Twitter 框架和完成处理程序块-“在此块中强烈捕获'自我'可能会导致保留周期”
【发布时间】:2011-12-30 20:25:05
【问题描述】:

我对编程和 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];                
                }            
            }

【问题讨论】:

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


    【解决方案1】:

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

    通过对 self 使用所谓的弱引用,块将不再保留对象。然后可以稍后释放对象,这将释放块(将 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? 上也有一个非常好的长解释,它还描述了如何在没有 ARC 的 iOS 4 上避免这种情况。

    【讨论】:

    • 感谢您的帮助!因为我是一个完全的初学者,所以我很难弄清楚如何真正实现这一点。最后我使用了 __weak UIImagePickerController *weakSelf = imagePickerController;并将我的 if 语句更改为 if([weakSelf sourceType])。 Xcode 4 不再向我显示任何错误,所以我想我做对了。 (?)
    • +1 @Dennis 感谢您的回答。还请说明何时使用“__block”。示例我的语法类似于 __block HomeViewController *weakSelf = self;
    【解决方案2】:

    您的块保留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。

    【讨论】:

    • 感谢您指出我的 UIAlertView 在块中有问题。由于我使用的是 ARC,我想 Xcode 告诉我我不能使用 __block。正如 TriPhoenix 提到的那样,我使用了 __weak ,效果很好。谢谢!
    • +1 @Terry Wilcox,请解释“__block”和“__weak”之间的区别。谢谢
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2013-01-11
        • 1970-01-01
        • 1970-01-01
        • 2013-03-16
        • 1970-01-01
        • 2011-11-04
        • 1970-01-01
        • 2015-02-04
        • 1970-01-01
        相关资源
        最近更新 更多