Захват себя в этом блоке, вероятно, приведет к циклу сохранения

Как я могу избежать этого предупреждения в xcode. Вот фрагмент кода:

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
    current+=1;

    if(current==60)
    {
        min+=(current/60);
        current = 0;
    }

    [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];

Ответ 1

Захват self здесь входит в ваш неявный доступ к ресурсу self.timerDisp - вы не можете ссылаться на self или свойства на self из блока, который будет сильно сохранен self.

Вы можете обойти это, создав слабую ссылку на self до доступа к timerDisp внутри вашего блока:

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];

Ответ 2

__weak MyClass *self_ = self; // that enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
    if (!error) {
       [self_ showAlertWithError:error];
    } else {
       self_.items = [NSArray arrayWithArray:receivedItems];
       [self_.tableView reloadData];
    }
};

И одна очень важная вещь, которую нужно помнить: не используйте переменные экземпляра непосредственно в блоке, используйте его как свойства слабого объекта, sample:

self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
        if (!error) {
           [self_ showAlertWithError:error];
        } else {
           self_.items = [NSArray arrayWithArray:receivedItems];
           [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
        }
 };

и не забывайте делать:

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}

может возникнуть другая проблема, если вы передадите слабую копию, не сохраненную кем-либо объектом:

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

если vcToGo будет освобожден, а затем этот блок выстрелил. Я считаю, что вы потеряете нераспознанный селектор в корзину, которая теперь содержит переменную vcToGo_. Попытайтесь его контролировать.

Ответ 3

Лучшая версия

__strong typeof(self) strongSelf = weakSelf;

Создайте сильную ссылку на эту слабую версию в качестве первой строки в вашем блоке. Если self все еще существует, когда блок начинает выполняться и не упал до нуля, эта строка гарантирует, что он будет сохраняться в течение всего срока выполнения блоков.

Итак, все будет так:

// Establish the weak self reference
__weak typeof(self) weakSelf = self;

[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                 queue:nil
                            usingBlock:^(CMTime time) {

    // Establish the strong self reference
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
    } else {
        // self doesn't exist
    }
}];

Я читал эту статью много раз. Это отличная статья Эрика Садун. Как избежать проблем при использовании блоков и NSNotificationCenter


Быстрое обновление:

Например, при быстром простом методе с блоком успеха будет:

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

Когда мы вызываем этот метод и нужно использовать self в блоке успеха. Мы будем использовать функции [weak self] и guard let.

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }

Этот так называемый сильно-слабый танец используется в популярном проекте с открытым исходным кодом Alamofire.

Для получения дополнительной информации swift-style-guide

Ответ 4

В другом ответе Тим сказал:

вы не можете ссылаться на себя или свойства на себя изнутри блока, который будет сильно сохранен сам.

Это не совсем так. Это нормально для вас, чтобы вы это делали, пока вы прерываете цикл в какой-то момент. Например, скажем, у вас есть таймер, который срабатывает, у которого есть блок, который сохраняет себя, и вы также сохраняете сильную ссылку на таймер в себе. Это прекрасно, если вы всегда знаете, что в какой-то момент вы уничтожите таймер и разорвите цикл.

В моем случае только сейчас у меня было это предупреждение для кода, который сделал:

[x setY:^{ [x doSomething]; }];

Теперь я знаю, что clang будет выдавать это предупреждение только в том случае, если он обнаруживает, что метод начинается с "set" (и еще одного специального случая, о котором я не упоминал здесь). Для меня я знаю, что нет никакой опасности, что существует цикл сохранения, поэтому я изменил имя метода на "useY:" Конечно, это может быть неприемлемо во всех случаях, и обычно вам нужно использовать слабую ссылку, но Я думал, что стоит отметить мое решение, если оно помогает другим.

Ответ 5

Добавление двух центов на улучшение точности и стиля. В большинстве случаев вы будете использовать только один или несколько членов self в этом блоке, скорее всего, просто обновите слайдер. Литье self является излишним. Вместо этого лучше быть явным и бросать только объекты, которые вам действительно нужны внутри блока. Например, если это экземпляр UISlider*, скажем, _timeSlider, просто выполните следующее перед объявлением блока:

UISlider* __weak slider = _timeSlider;

Затем просто используйте slider внутри блока. Технически это более точно, поскольку он сужает потенциальный цикл удержания только на предмет, который вам нужен, а не на все объекты внутри self.

Полный пример:

UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
     queue:nil
     usingBlock:^(CMTime time){
        slider.value = time.value/time.timescale;
     }
];

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

@property (nonatomic, weak) IBOutlet UISlider* timeSlider;

С точки зрения стиля кодирования, как и для C и С++, объявления переменных лучше читать справа налево. Объявление SomeType* __weak variable в этом порядке читается более естественно справа налево как: variable is a weak pointer to SomeType.

Ответ 6

Недавно я столкнулся с этим предупреждением и хотел понять его немного лучше. После небольшого проб и ошибок я обнаружил, что это происходит из-за того, что метод начинается с "добавить" или "сохранить". Задача C рассматривает имена методов, начинающиеся с "new", "alloc" и т.д., Как возвращающие сохраненный объект, но не упоминает (что я могу найти) что-либо о "add" или "save". Однако, если я использую имя метода таким образом:

[self addItemWithCompletionBlock:^(NSError *error) {
            [self done]; }];

Я увижу предупреждение в строке [self done]. Однако это не будет:

[self itemWithCompletionBlock:^(NSError *error) {
    [self done]; }];

Я пойду дальше и буду использовать способ "__weak __typeof (self) weakSelf = self", чтобы ссылаться на мой объект, но на самом деле мне не нравится делать это, так как это может сбить меня с толку и/или другого разработчика. Конечно, я также не мог использовать "добавить" (или "сохранить"), но это еще хуже, так как это убирает смысл метода.