5

Я сделал эти две коммунальные funcions:ARC поведение в рекурсивном блоке

+ (void)dispatch:(void (^)())f afterDelay:(float)delay { 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay*NSEC_PER_SEC)), 
        dispatch_get_main_queue(), 
        f); 
    } 

+ (void)dispatch:(void (^)())f withInterval:(float)delay { 
    void (^_f)() = nil; // <-- A 
    _f = ^{ 
     f(); 
     [self dispatch:_f afterDelay:delay]; // <-- B 
    }; 
    [self dispatch:_f afterDelay:delay]; 
} 

Идея заключается в том, что вы могли бы назвать:

[самостоятельной доставки: блок afterDelay: задержка]; - получить блок выполняется после определенного времени

и

[самостоятельная доставка: блока интервальный: задержки]; - получить блок выполняется периодически

Хорошо, теперь, если я позвоню отправки: интервальная:, как она есть, она будет создавать ошибку во время выполнения, потому что, когда программа пытается выполнить строку в B величины от _f будет nil; и это, в свою очередь, происходит потому, что _f имеет ссылку на значение _f по адресу A.

Это может быть исправлен, если я изменю к:

__block void (^_f)() = nil; 

и с этим я делаю сильную ссылку на _f, поэтому, когда код достигает B Значения _f - это окончательное значение, присвоенное ему. Проблема заключается в том, что я увлекаюсь циклом удержания.

Наконец, я могу изменить быть:

__block void (^_f)() __weak = nil; 

и должны заботиться обоих вопросов, однако я обнаружил, что, когда код достигает B Значение _f снова nil, потому что в момент его оценки _f уже освобожден.

У меня есть несколько вопросов:

  • На последнем сценарии, почему _f получить освобождаться? Как сообщить ARC о сохранении блока, по крайней мере, до следующего диспетчерского вызова?
  • Каким будет лучший (и ARC-совместимый) способ записи этих функций?

Спасибо за ваше время.

+0

Просто обратите внимание, что НОД имеет тип для 'пустоте (^) (ничтожной)' - 'dispatch_block_t'. –

+0

Спасибо, записал его – almosnow

ответ

4

Как сообщить ARC о сохранении блока, по крайней мере, до следующего диспетчерского вызова?

Я бы сказал, по методу, который вы используете с __block.

Проблема заключается в том, что я подвергаюсь циклу удержания.

Я не понимаю, почему это было проблемой. Вы хотите, чтобы ваш таймер стрелял бесконечно, не так ли? Это означает, что связанные с ним объекты должны жить вечно. Пока вы отправляете блок, он все равно сохраняется GCD, но наличие дополнительной ссылки, похоже, не повредит.

Если в какой-то момент в будущем вы решите отменить таймер, сделайте это, установив _f = nil. Это нарушит цикл сохранения.

Каким будет лучший (и совместимый с ARC) способ записи этих функций?

Ну, лучший способ - использовать NSTimer. Но я так думаю is интересно узнать, как использовать GCD. К счастью, у Apple есть a timer example here.

Хорошо, но не означает ли ссылка на _f приращиваться каждый раз, когда вызывается _f?

Давайте посмотрим, как работает __блок. Что делает система, создает глобальную переменную в куче и передает ссылку (скажем, указатель со значением A) в эту память на ваш блок (скажем, находящуюся в памяти значения B).

Итак, у вас есть память по адресу A, которая ссылается на память по адресу B, и наоборот. Как вы видите, здесь каждый объект имеет показатель сохранения 1; ну, GCD также сохраняется, но этот счетчик остается постоянным и не имеет причин увеличиваться.

Вы можете обнулить _f из другого места, а затем после НОД заканчивает блокировать сохраняют отсчет идет до 0.

почему это получить перераспределена, когда я использую __weak?

Как мы видели, есть две вещи, которые влияют на счетчик ARC объекта по адресу B: GCD и переменной _f. Если вы делаете _f слабым, то после присвоения ему ваш блок по-прежнему не имеет счет сохранения от _f, и у него нет счета из строки B, так как вы фактически не запустили блок. Таким образом, он немедленно освобождается.


Примечание. Это красота ARC: вы получите это поведение каждый раз, и здесь мы можем следить за всем, что происходит логически и выводить причину. С сборщиком мусора этот блок иногда освобождается, а иногда и нет, что делает отладку этой проблемы адом.

+0

Хорошо, но не ссылается ли ссылка на _f каждый раз при возрастании _f? И тогда, когда параметр _f равен нулю, будет ли он уменьшаться только один раз и, следовательно, вызвать утечку? Кроме того, почему он освобождается, когда я использую __weak? – almosnow

+0

Подробный комментарий добавлен в текст ответа. –

+0

Хорошо, и в случае, когда я использую __weak, я буду считать, что _f получает освобождение, потому что GCD сохраняет блок, но со слабой ссылкой, поэтому сильные ссылки на _f равны 0, и он получает GCed, я прав? – almosnow

1

_f должен быть сильной ссылкой, поскольку в противном случае в ARC блок, который ему назначен, может сразу исчезнуть, потому что нет сильных ссылок на него.

В то же время блоку необходимо получить доступ к указателю на себя, и, как вы обнаружили, это необходимо сделать с помощью переменной __block. Сильная ссылка от блока к себе приведет к циклу удержания, поэтому это должно быть слабой ссылкой.

Таким образом, вам необходимо две переменные, один сильный и один слабый:

+ (void)dispatch:(void (^)())f withInterval:(float)delay { 
    __block __weak void (^_f_weak)() = nil; // a weak __block variable for the block to capture 
    void (^_f)() = nil; // a strong variable to hold the block itself 
    _f_weak = _f = ^{ // both variables will point to the block 
     f(); 
     [self dispatch:_f_weak afterDelay:delay]; 
    }; 
    [self dispatch:_f afterDelay:delay]; 
} 
+0

Блестящий! Это именно то, что мне нужно, и это единственное решение, которое я нашел - после поиска часа! Xcode 7.3.1 предупреждает об утечках памяти, если у вас нет переменной __block __weak. –

Смежные вопросы