Avatar
omochimetaru 4/12/2023 2:17 AM
xxxChangesのステート3つだけ見ればよくて、idle/pending/activeで遷移するっぽいな
2:18 AM
これはobservationごとに管理されてて activeのときにdidSetが起きるとcontinuation.resumeで送信されてidleに戻る
2:19 AM
pendingのときにdidSetが起きると「ここまでの変更」にキーが追加される
2:19 AM
pendingってのが変更を継続的に記録して一つにまとめようとしている状態だ (edited)
2:20 AM
idleはresume送信後の初期状態で、ここでdidSetくるとpendingが始まる。
2:21 AM
insertNextChange が idle中に来るとactiveになる pending中に来ると、そこまで集めた変更(があれば)をresume送信してidleになる
2:21 AM
あーわかったぞ
2:23 AM
ObservedChangedはAsyncSequenceだから
2:23 AM
for-await-inで自動でnextが呼ばれるんだけど
2:23 AM
didSetでcontinuationが発火されるとそのnextがElementを返すんだけど
2:24 AM
その後forの中に入って、for本文でasync処理が実行されているまでの間が、idleなんだ
2:25 AM
で、for本文が終わって、またasync sequenceの次の値を読み取る準備ができたタイミングで、 やっとこさ next() が呼ばれるから
2:25 AM
その「次の next() が呼ばれるまでの間」に、発生したdidSetがpending stateとして累積されているんだわ (edited)
2:27 AM
didSetが発生するTaskと、for-awaitしてるTaskが別で並行に動いてるからズレが発生する、これがまとめられるって言ってる
2:28 AM
俺が疑問に思った withMutating のくくりだと毎回リセットされちゃうのではないか、は、同じアクターコンテキスト(Mainなど)においては多分そうで
2:29 AM
全部mainで組んだらプロパティに書き込むたびにイベントが来るんじゃないかな あ、でも送り側もmainでfor-await側もmainはありえないか、デッドロックするわ
2:34 AM
いやできるのか・・・?よくわからなくなってきた
2:35 AM
@MainActor func handleChanges(_ example: ChangesExample) { Task { @MainActor in for await change in example.changes(for: [\.someField, \.someOtherField]) { switch (change.contains(\.someField), change.contains(\.someOtherField)) case (true, true): print("Changed both properties") case (true, false): print("changed integer field") case (false, true): print("changed string field") default: fatalError("this case will never happen") } } } }
2:36 AM
↑これって、handleChangesはメインスレッドで呼ばれつつ、Taskがエンキューされて、 次のメインループでTask本文が実行されて、 example.changes().next が for-await によって呼ばれて・・・
2:37 AM
そうするとこの for-await は、MainActorでisolateされた本文コードだから、 async sequenceからエレメントが来たらMainActorにホップしてから処理されるけれど
2:37 AM
もし、この .next の結果を送信するための Continuationが、MainActorコンテキストなTaskから呼びされた場合は
2:38 AM
async sequenceの値送信側もmain actorで、for-awaitは、main actorからmain actorだからホップ無しで処理が進んで、 結果的に同期的にこのforの本文に入ってくるのかな。
2:38 AM
そうじゃないとUI更新が1フレーム遅れちゃうから困るんだけど。
2:40 AM
なんとなく以前からSwift Concurrencyで Task.init + for-await-in すると、1フレ遅れからは逃れられないように思ってたけど、 ちゃんとやれば大丈夫なんかね?