Вопрос по cocoa-touch, objective-c, automatic-ref-counting, retain, avplayer – сильный захват себя в этом блоке может привести к циклу сохранения

202

Как я могу избежать этого предупреждения в 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
}];
Какие'с этим:player(AVPlayer object) а также ?timerDisp(UILabel) Carl Veazey
AVPlayer * player; UILabel * timerDisp; user1845209
Да, @property (неатомный, сильный) UILabel * timerDisp; user1845209
ЯвляетсяtimerDisp собственность на класс? Tim
Настоящий вопрос в том, как заставить замолчать это предупреждение.без ненужная слабая ссылка на себя, когда вы знаете, что циклическая ссылка будет нарушена (например, если вы всегда очищаете ссылку, когда завершается сетевой запрос). Glenn Maynard

Ваш Ответ

7   ответов
500

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]];
                                            }];
Оно работает. ios9.2 pkc456
Это помогло мнеstackoverflow.com/questions/7205128/... iCoder86
@erikprice: тыне ошибаюсь. Я интерпретировал этот вопрос, прежде всего, об ошибке, которую представляет XCode ("Как я могу избежать этого предупреждения в xcode "), а не о фактическом наличии цикла сохранения. Вы'Правильно сказать, что цикл сохранения не виден только из предоставленного фрагмента OP. Tim
В моем примере проекта, похоже, что это позволит избежать этого предупреждения. Имейте в виду, однако, что вы все еще временно устанавливаете цикл сохранения - так как вашstrongSelf является (как вы говорите) сильным,self и блок сохранит друг друга. настройкаstrongSelf вnil следует разорвать цикл, хотя. Применим ли этот подход к вашей ситуации, оставляем на усмотрение каждого. Tim
38
Лучшая версия
__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

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

Например, в swift простой метод с успешным блоком будет:

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

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

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

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

Для получения дополнительной информации проверьтескор-стиль-гид

Чтобы избежать возможных циклов сохранения, мы устанавливаем слабую ссылку на себя вне любого блока, который использует себя в своем коде. В вашем случае, вы должны убедиться, что блок выполняется. Другой блок вашего кода теперь отвечает за освобождение ранее сохраненной памяти. Warif Akhand Rishi
Что делать, если вы сделалиtypeof(self) strongSelf = self; за пределами блока (вместо __weak), то в блоке сказалstrongSelf = nil; после использования? Я нене вижу, как ваш пример гарантирует, чтоt nil к моменту выполнения блока. Matt
@Matt цель этого примера не состоит в том, чтобы сохранить слабый элемент. Цель состоит в том, чтобы, если strongSelf не равен nil, создать сильную ссылку внутри блока. Таким образом, как только блок начинает выполняться с собой, сам неСтало ноль внутри блока. Warif Akhand Rishi
15

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

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

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

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

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

1

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

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

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

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

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

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

не используйте переменные экземпляра непосредственно в блоке, используйте его как свойства слабого объекта, образца:

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_ переменная сейчас. Попробуйте это контролировать.

Это будет более сильный ответ, если вы также объясните это. Eric J.
2

это на самом деле не цикл сохранения.

Если вы знаете, что этоНет, вам не нужно приносить бесплодных слабых себя в мир.

Apple даже навязывает нам эти предупреждения с помощью API ихUIPageViewController, который включает в себя метод set(который вызывает эти предупреждения–как упоминалось в другом месте -думая, что вы устанавливаете значение для ивара, который является блоком) и блок обработчика завершения (в котором выБуду несомненно ссылаться на себя).

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

#pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
    [self.pageViewController setViewControllers:@[newViewController] direction:navigationDirection animated:YES completion:^(BOOL finished) {
        // this warning is caused because "setViewControllers" starts with "set…", it's not a problem
        [self doTheThingsIGottaDo:finished touchThePuppetHead:YES];
    }];
#pragma GCC diagnostic pop
1

учаев вы будете использовать только один или несколько членов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 и C ++, объявления переменных лучше читать справа налево. декларированиеSomeType* __weak variable в этом порядке читается более естественно справа налево как:.variable is a weak pointer to SomeType

Похожие вопросы