我是编程和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];
}
}
答案 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”开头时,编译器似乎才会发出此警告。