繁体   English   中英

iOS 5 Twitter Framework和completionHandler块 - “在这个块中强烈捕获'自我'可能会导致保留周期”

[英]iOS 5 Twitter Framework & completionHandler block - “Capturing 'self' strongly in this block is likely to lead to a retain cycle”

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

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

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

基本上,我所做的是删除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];                
                }            
            }

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

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

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

在你的块内你现在可以使用weakSelf而不是self 请注意,这仅适用于使用ARC的iOS 5。

看一下这里还有一个非常好的长解释, 如何在实现API时避免在块中捕获self? 其中还介绍了如何在iOS 4和没有ARC的情况下避免这种情况。

您的块正在保留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文档对象和块变量部分 除非使用__block,否则将保留块中引用的对象。

根据我在其他地方看到过的东西,“ 弱”对于符合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];
                                        }];

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM