1. ホーム
  2. swift

[解決済み] 弱い自分はどこへ行くのか?

2023-04-20 03:06:40

質問

よくこんなことをします。

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   beep()
}

とあるアプリでは、よくこんなことをやっています。

tickle.fresh(){
    msg in
    paint()
}

が、もし これ

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   tickle.fresh(){
      msg in
      paint()
   }
}

を行う必要があります。 これ

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) { [weak self] _ in
   tickle.fresh(){
      msg in
      self?.paint()
   }
}

または、もしかしたら これ

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   tickle.fresh(){
      [weak self] msg in
      self?.paint()
   }
}

あるいは、これ

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) { [weak self] _ in
   tickle.fresh(){
      [weak self] msg in
      self?.paint()
   }
}

どうすればいい?

3つの提案のすべて と思われる は完璧に機能するように見えます。ここでの意味の完全な深さは何なのでしょうか?そして、どちらを行うべきでしょうか?弱い参照への強い参照は、弱い参照なのか強い参照なのか?To be か not to be か? それが問題だ!

どのように解決する?

まず第一に、一般に、retain cyclesについて心配する必要がないことに注意してください。 DispatchQueue.main.asyncAfter でクロージャが実行されるからです。 いくつかの 点で実行されるからです。したがって、弱く捕捉するかどうかに関わらず self を弱く捕捉しようがしまいが、永久に保持するサイクルは作らない(と仮定して tickle.fresh もそうでないと仮定します)。

を付けるかどうかは別として [weak self] キャプチャリストを外側の asyncAfter クロージャが必要かどうかで決まります。 self をクロージャが呼ばれるまで(設定された時間の後)保持したいかどうかによります。もし self がクロージャが呼ばれるまで生きている必要があるならば、 クロージャに [weak self] を入れるか、そうであれば入れないか。

を入れるかどうかは [weak self] をつけるかどうか、また、内側のクロージャ( tickle.fresh を外側のクロージャで捕捉しているかどうかに依存します。もしそうでないなら、外側のクロージャに self を置くことで、内側のクロージャがそれを保持しないようにします。しかし、もし外側のクロージャがすでに弱く捕捉していれば [weak self] を捕捉していた場合、内側のクロージャーは すでに への弱い参照を持っています。 self への参照は弱いので self を追加しても効果はありません。

というわけで、まとめると


[weak self]

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { tickle.fresh { msg in self.paint() } } は外側と内側の両方のクロージャによって保持されます。


self

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in tickle.fresh { msg in self?.paint() } } はどちらのクロージャでも保持されません。


self

上記と同様に、追加の DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in tickle.fresh { [weak self] msg in self?.paint() } } を追加しても効果はありません。 [weak self] はすでに外側のクロージャによって弱く捕捉されているため、効果はありません。


self

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { tickle.fresh { [weak self] msg in self?.paint() } } は外側クロージャで保持されますが、内側クロージャでは保持されません。


もちろん、あなたが必要としないかもしれない self を外側のクロージャに保持させたくないが を行う は内側のクロージャに保持させたい。このような場合、外側のクロージャでローカル変数を宣言することで self への強い参照を保持するために、外側のクロージャでローカル変数を宣言し、内側のクロージャでキャプチャすることができます。

self

今すぐ DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in guard let strongSelf = self else { return } tickle.fresh { msg in strongSelf.paint() } } は外側のクロージャによって生かされることはありませんが、いったん呼び出されると self がまだ存在していれば、内側のクロージャによって、そのクロージャが解放されるまで生かされます。


に対応しています。

<ブロッククオート

弱い参照への強い参照は、弱い参照と強い参照のどちらでしょうか?

弱参照は、値型であるoptionalとして実装されています。そのため、以下のようなことはできません。 を直接 その代わりに、まずそれをアンラップし、基礎となるインスタンスへの強い参照を取らなければなりません。この場合、単に強い参照を扱っているに過ぎません。 self ).

ただし、弱参照が ボックス化 (これはクロージャ キャプチャで発生します。値の型はヒープで割り当てられたボックスに入れられます) - そのボックスへの強い参照を実際に持つことができます。この効果は、元のインスタンスへの弱い参照と同等であり、目に見えない追加の間接的なビットを持つだけです。

実際、これは まさに のように、外側のクロージャが弱く捕捉する例で起こることです。 strongSelf であり、内側のクロージャはその弱い参照を「強く捕捉」している。その結果、どちらのクロージャーも self .