4

Так вот что я получил:Создание контура с использованием GCD

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), ^{ 
       bool ready = some_function(); 
       if(ready) {      
        do_smth_here() 
       } else { 
        //invoke this block one more time after 0.1 sec 
       } 
      }); 

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

ответ

4

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

До тех пор, пока создание блока не является ужасно дорогостоящим - чего не будет, если государство исходит из того, что инкапсулирует метод экземпляра - оно достаточно эффективно и чертовски много проще.

- (void) retriggerMethod 
{ 
    ... do stuff here, assuming you want to do it on first invocation ... 
    dispatch_after(..., ^{ 
     [self retriggerMethod]; 
    }); 
} 

Вы можете перестроить его по мере необходимости. И вы можете легко добавить переменную экземпляра BOOL, если вы хотите защитить от одновременных ретриггеров и т. Д.

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

+0

Похоже, вы часто используете цикл GCD. Интересно, в чем преимущества его использования, а не в установке NSTimer? – Gon

+0

@Gon Личные предпочтения, действительно, но есть некоторые отличия. 'dispatch _ *()' позволяет вам жестко управлять очередью, например. Поскольку я ленив, мне также очень нравятся фрагменты, вставленные дополнением кода Xcode. :) – bbum

1

Я думаю, что это код ваш ищет:

__block void (^callback)(); 
callback = ^{ 
    bool ready = some_function(); 
    if(ready) { 
     do_smth_here() 
    } else { 
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 
    } 
}; 

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 

Благодаря ^ Blocks Tips & Tricks

+0

@robmayoff Leak? Я этого не вижу. Здесь нет цикла удержания. 'callback' автоматически сохраняется в' dispatch_after() 'и автоматически освобождается, когда блок завершается. Все остатки и релизы сокращаются. Я не сомневаюсь, что может быть некоторый дефект в реализации блоков, которые будут содержать дополнительную ссылку, но я должен был бы увидеть это в анализаторе. –

+0

См. Мой ответ. BTW, я протестировал, что ваше решение вылетает (под MRC) и утечки (при ARC), используя журнал регистрации malloc. –

+0

@robmayoff Что я могу сказать? Компилятор и среда исполнения являются конечным судьей. Хорошее решение. –

3

ответ Джеффри Томас близок, но в АРК, он попадает в блок, и без АРК, он выходит из строя.

Без ARC переменная __block не сохраняет то, что она ссылается. Блоки создаются в стеке. Таким образом, переменная callback указывает на блок в стеке. Когда вы передаете callback в dispatch_after в первый раз (за пределами блока), dispatch_after успешно делает копию блока в куче. Но когда эта копия вызывается, а затем возвращается callback в dispatch_after, callback - это оборванный указатель (к теперь уничтоженному блоку в стеке), а dispatch_after будет (обычно) сбой.

С АРК, A __block переменных типа блока (например, callback) автоматически копирует блок в кучу. Таким образом, вы не получите аварии. Но с ARC переменная __block сохраняет объект (или блок), который он ссылается. Это приводит к циклу сохранения: блок ссылается на себя. Xcode покажет вам предупреждение о рекурсивном вызове dispatch_after: «Захват« обратного вызова »сильно в этом блоке, вероятно, приведет к циклу сохранения».

Чтобы устранить эти проблемы, вы можете скопировать блок явно (переместить его из стека в кучу под MRC) и установите callback к нулю (по АРК), либо освободить его (под MRC), чтобы предотвратить утечку его:

__block void (^callback)() = [^{ 
     if(stop_) { 
      NSLog(@"all done"); 
#if __has_feature(objc_arc) 
      callback = nil; // break retain cycle 
#else 
      [callback release]; 
#endif 
     } else { 
      NSLog(@"still going"); 
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 
     } 
    } copy]; 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 

Очевидно, что вы можете удалить #if и просто использовать ветку, подходящую для управления вашей памятью.

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