简体   繁体   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”

I am very new to programming and Objective-C and I am trying to work out what is wrong with my code. 我是编程和Objective-C的新手,我正在尝试解决我的代码有什么问题。 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. 我的代码使用的是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. 我使用Apple提供的大多数示例代码,所以我实际上一开始并不知道我使用了一个块作为完成处理程序。

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

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] . 基本上,我所做的是删除Apple在其完成处理程序中使用的代码(使用TWTweetComposeViewControllerResultCancelled和TWTweetComposeViewControllerResultDone的switch语句)并使用我的if语句与[imagePickerController sourceType]

So the sendTweet gets called after an image has been added to the tweet. 因此,在将图像添加到推文后调用sendTweet

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

The basic problem is that you're using self within a block. 基本问题是你在一个街区内使用self 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: Fortunaly有一个简单的解决方法:

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): 然后可以释放该对象,该对象将释放该块(将MyClass设置为适当的类型):

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

Inside your block you now can use weakSelf instead of self . 在你的块内你现在可以使用weakSelf而不是self Note that this is only for iOS 5 using ARC. 请注意,这仅适用于使用ARC的iOS 5。

Looking at this there is also a very good long explanation on How do I avoid capturing self in blocks when implementing an API? 看一下这里还有一个非常好的长解释, 如何在实现API时避免在块中捕获self? which also describes how to avoid this on iOS 4 and without ARC. 其中还介绍了如何在iOS 4和没有ARC的情况下避免这种情况。

Your block is retaining self because you're using self as the delegate of the UIAlertView. 您的块正在保留self因为您使用self作为UIAlertView的委托。 If self is also retaining the block, they're retaining each other, which creates a retain cycle. 如果self也保留了块,它们会相互保留,从而形成一个保留周期。

Try 尝试

 __block WhateverTypeSelfIs *nonRetainedSelfForBlock = self;
[tweetViewController setCompletionHandler: 

and

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

Check the Apple docs , section Object and Block Variables . 检查Apple文档对象和块变量部分 Objects referenced within a block are retained, unless you use __block. 除非使用__block,否则将保留块中引用的对象。

According to stuff I've seen elsewhere, “ weak” won't work for ARC-compliant code, and that you must use “ _unsafe_unretained” instead. 根据我在其他地方看到过的东西,“ 弱”对于符合ARC的代码不起作用,而且你必须使用“ _unsafe_unretained”。 This is what I did to fix the “Capturing 'self' strongly in this block is likely to lead to a retain cycle” warning in the Apple sample app "AVPlayerDemo": 这就是我在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];
                                        }];

There is a rather surprising way to get rid of the warning. 有一种相当令人惊讶的方法来摆脱警告。 Mind you, only do this if you are sure you are not creating a retain cycle, or you are sure that you will break it yourself later on (by setting the completion handler to nil when you're done, for example). 请注意,只有在确定自己没有创建保留周期时才这样做,或者您确定稍后会自行解除(例如,在完成后将完成处理程序设置为nil)。 If you have control over the interface, then rename your method so it does not start with "set". 如果您可以控制界面,则重命名您的方法,使其不以“set”开头。 The compiler seems to give this warning only when the method name starts with "set". 仅当方法名称以“set”开头时,编译器似乎才会发出此警告。

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

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