iOS 5 Twitter Framework и завершение блокаHandler - «Сильный захват себя» в этом блоке может привести к циклу сохранения » - PullRequest
10 голосов
/ 17 ноября 2011

Я очень новичок в программировании и 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];                
                }            
            }

Ответы [ 4 ]

21 голосов
/ 17 ноября 2011

Основная проблема в том, что вы используете self в блоке. Блок сохраняется объектом, и сам блок также сохраняет объект. Таким образом, у вас есть сохраняющий цикл, и поэтому оба, вероятно, никогда не будут выпущены, потому что оба имеют ссылку, указывающую на них. К счастью, существует простой обходной путь:

При использовании так называемой слабой ссылки на себя блок больше не будет удерживать объект. Затем объект может быть впоследствии освобожден, что освободит блок (установите MyClass на соответствующий тип):

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

Внутри вашего блока теперь вы можете использовать weakSelf вместо self. Обратите внимание, что это только для iOS 5 с использованием ARC.

Глядя на это, есть также очень хорошее подробное объяснение Как избежать захвата себя в блоках при реализации API? , в котором также описано, как этого избежать на iOS 4 и без ARC.

3 голосов
/ 17 ноября 2011

Ваш блок сохраняет 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.

1 голос
/ 24 мая 2012

Согласно материалам, которые я видел в других местах, « слабый» не будет работать для кода, совместимого с 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];
                                        }];
0 голосов
/ 03 марта 2012

Существует довольно удивительный способ избавиться от предупреждения.Имейте в виду, делайте это только в том случае, если вы уверены, что не создаете цикл сохранения, или уверены, что позже вы его сами прервете (например, установив обработчик завершения на ноль, когда вы закончите).Если у вас есть контроль над интерфейсом, переименуйте ваш метод, чтобы он не начинался с «set».Компилятор, похоже, выдает это предупреждение только тогда, когда имя метода начинается с «set».

...