IOS 5 Twitter Framework и блок CompletionHandler - "Захват" я "сильно в этом блоке, вероятно, приведет к циклу сохранения"
Я очень новичок в программировании и Objective-C, и я пытаюсь выяснить, что не так с моим кодом. Я немного читал о блоках, но я не знаю, как какое-либо из того, что я прочитал до сих пор, имеет отношение к моему коду.
Мой код использует iOS 5 Twitter Framework. Я использую большую часть образца кода, который Apple предоставляет, поэтому на самом деле я вообще не знал, что я использовал блок для обработчика завершения.
Теперь я получаю эти два сообщения из Xcode 4, говоря "1. Блок будет сохранен объектом, сильно сохраненным захваченным объектом" и "Сильная фиксация" я "в этом блоке, вероятно, приведет к циклу сохранения".
В основном, я сделал, чтобы удалить код, который Apple использовал в своем обработчике завершения (оператор switch с TWTweetComposeViewControllerResultCancelled и TWTweetComposeViewControllerResultDone) и использовал мои операторы 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];
}
}
Ответы
Ответ 1
Основная проблема заключается в том, что вы используете self
внутри блока. Блок сохраняется объектом, а сам блок также сохраняет объект. Таким образом, у вас есть цикл сохранения, и, следовательно, оба они, вероятно, никогда не будут выпущены, потому что у обоих есть ссылка, указывающая на них. В Fortunaly есть простой способ:
Используя так называемую слабую ссылку на себя, блок больше не будет сохранять объект. Затем объект затем может быть освобожден, который освободит блок (установите MyClass
на соответствующий тип):
// before your block
__weak MyObject *weakSelf = self;
Внутри вашего блока теперь вы можете использовать weakSelf
вместо self
. Обратите внимание, что это только для iOS 5 с использованием ARC.
Глядя на это, есть также очень хорошее длинное объяснение Как избежать захвата себя в блоках при реализации API?, в котором также описывается, как избежать этого iOS 4 и без ARC.
Ответ 2
Ваш блок сохраняет self
, потому что вы используете self
в качестве делегата UIAlertView. Если self
также сохраняет блок, они сохраняют друг друга, что создает цикл сохранения.
Try
__block WhateverTypeSelfIs *nonRetainedSelfForBlock = self;
[tweetViewController setCompletionHandler:
и
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:alertTitle
message:alertMessage
delegate:nonRetainedSelfForBlock
cancelButtonTitle:alertCancelButtonTitle
otherButtonTitles:otherAlertButtonTitle,nil];
Проверьте Документы Apple, раздел Переменные объектов и блоков. Объекты, на которые ссылаются внутри блока, сохраняются, если вы не используете __block.
Ответ 3
Согласно материалам, которые я видел в другом месте, "слабый" не будет работать для кода, совместимого с ARC, и вместо этого вы должны использовать "_unsafe_unreeded". Это то, что я сделал, чтобы зафиксировать "захват" себя в этом блоке, вероятно, приведет к предупреждению цикла сохранения в примере приложения 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];
}];
Ответ 4
Существует довольно удивительный способ избавиться от предупреждения. Имейте в виду, делайте это только в том случае, если вы уверены, что не создаете цикл сохранения, или вы уверены, что позже вы его разломете (например, установив обработчик завершения на нуль, когда вы закончите). Если у вас есть контроль над интерфейсом, переименуйте свой метод, чтобы он не начинался с "set". Компилятор, похоже, дает это предупреждение, только когда имя метода начинается с "set".