web-dev-qa-db-ja.com

弱い自己はどこへ行くのですか?

私はこれをよくします

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

そして、1つのアプリでこれを頻繁に行います

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

しかし、あなたがthis

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

もちろん、あなたはしなければなりませんthis

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

または、多分this

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()
   }
}

W T Hすべきですか?

3つの提案すべてseem完全に動作します。ここでの意味の完全な深さは何ですか?そして、どれをするべきですか?弱い参照への強い参照、弱い参照、または強い参照ですか?あるかどうかそれが問題です!

39
Fattie

まず、クロージャーはsomeポイントで実行されるため、一般的にDispatchQueue.main.asyncAfterの保持サイクルについて心配する必要はないことに注意してください。したがって、selfを弱くキャプチャするかどうかに関係なく、永続的な保持サイクルを作成することはありません(tickle.freshもそうではないと想定)。

外側のasyncAfterクロージャーに[weak self]キャプチャリストを置くかどうかは、クロージャーが呼び出されるまで(設定した時間の後)selfを保持するかどうかに完全に依存します。クロージャが呼び出されるまで生き続けるためにselfを必要としないなら、[weak self]を入れてください。そうするなら、入れないでください。

[weak self]を内側のクロージャー(tickle.freshに渡されるもの)に配置するかどうかは、外側のクロージャーでselfを既に弱くキャプチャーしているかどうかによって異なります。まだ持っていない場合は、[weak self]を配置して、内部のクロージャーが保持しないようにすることができます。ただし、外側のクロージャーがselfを既に弱くキャプチャしている場合、内側のクロージャーはalreadyselfへの弱い参照を持つため、[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()
   }
}

上記と同じように、selfは外側のクロージャーによって既に弱くキャプチャされているため、内側のクロージャーに追加の[weak self]は効果がありません。


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
   tickle.fresh { [weak self] msg in
      self?.Paint()
   }
}

selfは、外側のクロージャーによって保持されますが、内側のクロージャーによって保持されません。


もちろん、selfを外側のクロージャーで保持したくないが、do内側のクロージャーで保持したい場合があります。そのような場合、selfへの強い参照を保持するために、外側のクロージャーでローカル変数を宣言できます。その後、内側のクロージャーでキャプチャできます。

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
   guard let strongSelf = self else { return }
   tickle.fresh { msg in
      strongSelf.Paint()
   }
}

これで、selfは外側のクロージャーによって保持されなくなりますが、一度呼び出されると、selfがまだ存在する場合、そのクロージャーの割り当てが解除されるまで内側のクロージャーによって保持されます。


に応答して:

弱い参照への強い参照、弱い参照、または強い参照ですか?

弱い参照は、値の型であるオプションとして実装されます。したがって、直接に強い参照を付けることはできません。代わりに、最初にそれをアンラップしてから、基になるインスタンスへの強い参照を取得する必要があります。この場合、単純に強い参照を処理しています(strongSelfを使用した上記の例とまったく同じです)。

ただし、弱参照がboxedである場合(これはクロージャーキャプチャで発生します-値型はヒープに割り当てられたボックスに入れられます)–そのボックスへの強い参照を実際に持つことができます。この効果は、元のインスタンスへの弱い参照に相当し、目に見えない余分な間接性があります。

実際、これはexactlyです。外側のクロージャーがselfを弱くキャプチャし、内側のクロージャーがその弱い参照を「強くキャプチャ」する例で何が起こるかです。その結果、どちらのクロージャーもselfを保持しません。

102
Hamish