как запланировать запуск блока на следующей итерации цикла выполнения?

<p>я хочу иметь возможность выполнять <code>block</code> на следующей итерации цикла выполнения. не так важно, выполняется ли это в начале или в конце следующего цикла выполнения, просто это выполнение откладывается до тех пор, пока не завершится выполнение всего кода в текущем цикле выполнения.</p><p>я знаю, что следующее не работает, потому что оно чередуется с основным циклом выполнения, поэтому мой код может выполняться в следующем цикле выполнения, но это может и не выполняться.</p><pre><code>dispatch\_async(dispatch\_get\_main\_queue(),^{ //my code }); </code></pre><p>следующее, я считаю, страдает той же проблемой, что и выше:</p><pre><code>dispatch\_after(dispatch\_time\_now, dispatch\_get\_main\_queue(), ^(void){ //my code }); </code></pre><p>теперь я<em> полагаю </em>следующее будет работать, поскольку оно помещается в конец текущего цикла выполнения (поправьте меня, если я ошибаюсь), будет ли это на самом деле работать?</p><pre><code>[self performselector:@selector(mymethod) withobject:nil afterdelay:0]; </code></pre><p>как насчет таймера с интервалом <code>0</code>? документация гласит: <code>if seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead.</code> означает ли это гарантировать выполнение на следующей итерации цикла выполнения?</p><pre><code>[nstimer scheduledtimerwithtimeinterval:0 target:self selector:@selector(mymethod) userinfo:nil repeats:no]; </code></pre><p>это все варианты, которые я могу придумать, но я все еще не ближе к выполнению блока (в отличие от вызова метода) на следующей итерации цикла выполнения с гарантией того, что это произойдет не раньше.</p>

<p>вы можете не знать обо всем, что цикл выполнения выполняет в каждой итерации. (я не был до того, как я исследовал этот ответ!) как это происходит, <code>cfrunloop</code> является частью <a href="http://opensource.apple.com/source/cf/cf-744.12/">пакета с открытым исходным кодом corefoundation</a> , поэтому мы можем взглянуть точно, что это влечет за собой. цикл выполнения выглядит примерно так:</p><pre class="lang-none prettyprint-override"><code>while (true) { call kcfrunloopbeforetimers observer callbacks; call kcfrunloopbeforesources observer callbacks; perform blocks queued by cfrunloopperformblock; call the callback of each version 0 cfrunloopsource that has been signalled; if (any version 0 source callbacks were called) { perform blocks newly queued by cfrunloopperformblock; } if (i didn't drain the main queue on the last iteration and the main queue has any blocks waiting) { while (main queue has blocks) { perform the next block on the main queue } } else { call kcfrunloopbeforewaiting observer callbacks; wait for a cfrunloopsource to be signalled or for a timer to fire or for a block to be added to the main queue; call kcfrunloopafterwaiting observer callbacks; if (the event was a timer) { call cfrunlooptimer callbacks for timers that should have fired by now } else if (event was a block arriving on the main queue) { while (main queue has blocks) { perform the next block on the main queue } } else { look up the version 1 cfrunloopsource for the event if (i found a version 1 source) { call the source's callback } } } perform blocks queued by cfrunloopperformblock; } </code></pre><p>вы можете видеть, что есть множество способов подключиться к циклу выполнения. вы можете создать <code>cfrunloopobserver</code>, который будет вызываться для любой «деятельности», которую вы хотите. вы можете создать версию 0 <code>cfrunloopsource</code> и немедленно сообщить об этом. вы можете создать подключенную пару <code>cfmessageports</code>, обернуть ее в версию 1 <code>cfrunloopsource</code> и отправить ей сообщение. вы можете создать <code>cfrunlooptimer</code>. вы можете поставить в очередь блоки, используя <code>dispatch\_get\_main\_queue</code> или <code>cfrunloopperformblock</code>.</p><p>вам нужно будет решить, какой из этих api использовать, основываясь на том, когда вы планируете блок и когда вам нужно, чтобы он вызывался.</p><p>например, касания обрабатываются в источнике версии 1, но если вы обрабатываете касание путем обновления экрана, это обновление фактически не выполняется, пока транзакция core animation не будет зафиксирована, что происходит в наблюдателе <code>kcfrunloopbeforewaiting</code>.</p><p>теперь предположим, что вы хотите запланировать блок во время обработки касания, но хотите, чтобы он выполнялся после фиксации транзакции.</p><p>вы можете добавить свой собственный <code>cfrunloopobserver</code> для действия <code>kcfrunloopbeforewaiting</code>, но этот наблюдатель может запускаться до или после наблюдателя core animation, в зависимости от того, какой порядок вы указали и какой порядок указывает core animation. (базовая анимация в настоящее время определяет порядок 2000000, но это не задокументировано, поэтому оно может измениться.)</p><p>чтобы убедиться, что ваш блок работает после наблюдателя core animation, даже если ваш наблюдатель запускается <em>до</em> наблюдателя core animation, не вызывайте блок непосредственно в обратном вызове вашего наблюдателя. вместо этого используйте <code>dispatch\_async</code> в этой точке, чтобы добавить блок в основную очередь. помещение блока в основную очередь заставит цикл выполнения немедленно выйти из режима ожидания. он будет запускать любых наблюдателей <code>kcfrunloopafterwaiting</code>, а затем опустошит основную очередь, после чего будет запущен ваш блок.</p>

<p>ответ роба великолепен и информативен. я не пытаюсь заменить это.</p><p>просто читая <a href="https://developer.apple.com/documentation/uikit/uiview/1622515-animatewithduration?language=objc">документацию uiview</a> , я нашел:</p><blockquote> <p><strong>завершение</strong> </p> <p>блок-объект для выполнения при анимации последовательности заканчивается. этот блок не имеет возвращаемого значения и принимает один логический аргумент, который указывает, действительно ли анимация завершено до вызова обработчика завершения. если продолжительность анимация равна 0, этот блок выполняется в начале цикл следующего цикла. этот параметр может быть недействителен.</p> </blockquote><p>таким простым решением будет:</p><pre><code>uiview.animate(withduration: 0) { // anything } </code></pre>

<p>я не верю, что существует какой-либо api, который позволит вам гарантировать выполнение кода при следующем повороте цикла событий. мне также любопытно, почему вам нужна гарантия, что в цикле больше ничего не запущено, в частности главное.</p><p>я также могу подтвердить, что при использовании perforselector: withobject: afterdelay действительно используется таймер на основе runloop, и он будет иметь функциональное поведение, аналогичное dispatch\_async'ing для dispatch\_get\_main\_queue ().</p><p>правка:</p><p>на самом деле, после повторного прочтения вашего вопроса, похоже, что для завершения вам нужен только поворот <em>current</em> runloop. если это правда, то dispatch\_async - это именно то, что вам нужно. фактически, весь приведенный выше код дает гарантию, что очередь <em>current</em> завершится.</p>