引数のあるSwiftブロックで弱い自己を正しく処理する方法


151

私のTextViewTableViewCellには、ブロックを追跡する変数と、ブロックが渡されて割り当てられるconfigureメソッドがあります。
これが私のTextViewTableViewCellクラスです:

//
//  TextViewTableViewCell.swift
//

import UIKit

class TextViewTableViewCell: UITableViewCell, UITextViewDelegate {

    @IBOutlet var textView : UITextView

    var onTextViewEditClosure : ((text : String) -> Void)?

    func configure(#text: String?, onTextEdit : ((text : String) -> Void)) {
        onTextViewEditClosure = onTextEdit
        textView.delegate = self
        textView.text = text
    }

    // #pragma mark - Text View Delegate

    func textViewDidEndEditing(textView: UITextView!) {
        if onTextViewEditClosure {
            onTextViewEditClosure!(text: textView.text)
        }
    }
}

私のcellForRowAtIndexPathメソッドでconfigureメソッドを使用する場合、渡すブロックで弱い自己を適切に使用するにはどうすればよい
ですか?弱い自己がない場合は次のようになります。

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {(text: String) in
   // THIS SELF NEEDS TO BE WEAK  
   self.body = text
})
cell = bodyCell

更新:私は以下を使用して動作するようになりました[weak self]

let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {[weak self] (text: String) in
        if let strongSelf = self {
             strongSelf.body = text
        }
})
cell = myCell

ステートメントの[unowned self]代わりに[weak self]それを実行するとif、アプリがクラッシュします。これがどのように機能するかについてのアイデアはあります[unowned self]か?


それでは、以下の答えを正解として選んでいただけますか?また、所有していない場合は、閉鎖内で自己を強化する必要はありません。セルのライフサイクルとビューコントローラーはリンクされているため、ここでは所有されていない方が弱いよりも優れています。
ikuramedia 14

1
[無名の自己]の方が良いオプションだとわかりましたが、使用するとアプリがクラッシュします。答えを締めくくるためにそれを使用するコードサンプルを見たいです。
NatashaTheRobot 14

1
ドキュメントから:「弱い参照のように、所有されていない参照は、それが参照するインスタンスを強力に保持しません。ただし、弱い参照とは異なり、所有されていない参照は常に値を持つと想定されます。」アプリがクラッシュした場合、おそらく、実行時に所有されていない値がnilの値に適用されているためです。
Bill Patterson

strongSelfにバインドさせるよりも、ここでガードステートメントをアドバタイズする方が良いでしょう。ただ言うだけで、これは完璧な候補者のようなものです:-D
Daniel Galasko

@NatashaTheRobot、[弱い自己]とはどのような構文ですか。目的Cを渡すメッセージのように見えます。質問に構文についてもう少し追加してください。
Vignesh 2016

回答:


178

クロージャでセルフがnilの場合は、[weak self]を使用します。

クロージャーで自分自身がnilにならない場合は、[unowned self]を使用します。

[unowned self]を使用するとクラッシュする場合、そのクロージャーのある時点でselfがnilであると思います。そのため、代わりに[weak self]を使用する必要がありました。

クロージャでstrongweak、そしてunownedを使用することに関するマニュアルのセクション全体が本当に気に入りました:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

注:新しいSwiftの用語であるブロックではなく、クロージャーという用語を使用しました。

iOSでのブロック(Objective C)とクロージャー(Swift)の違い


7
Appleは、C言語拡張の最初の文書でブロックを「クロージャ」と呼んだ。(ブロックまたはクロージャは、最初はCの拡張です。Objective-Cに関連しているのはMMだけです。)Cの「ブロック」は複合ステートメントに非常に頻繁に関連しているため、私も「クロージャ」という用語を好みます。オブジェクト(変数または定数)を閉じなくてもクロージャと呼ばれるため、両方の言語で一種の誤りです。
Amin Negm-Awad 2015

1
非常にうまく答え:)
iDevAmit 2017年

1
を使用しないことをお勧めしunownedます。アプリをクラッシュさせるリスクはありません。
カイル・レッドフェーン

32

あなたの閉鎖の[unowned self](text: String)...に置きます。これはキャプチャリストと呼ばれ、クロージャでキャプチャされたシンボルに所有権の指示を配置します。


2
名前を付けてくれてありがとう、知りたい!
rob5408

3
この答えは役に立たないと思います。[無名の自己]は、閉鎖の実行中に自己がnilになるとクラッシュします
Yunus Nedim Mehel

3
(1)非常に異常な状況でのパフォーマンス(ここでは、プログラミングの99.999%ではまったく関係ありません)と(2)スタイル適用の問題として、非所有を使用する理由まったくありません。「常に弱く、決して所有されていないものを使用する必要があります」という文は非常に合理的です。
Fattie

29

** Swift 4.2用に編集:

@Koenがコメントしたように、Swift 4.2では次のことが可能です:

guard let self = self else {
   return // Could not get a strong reference for self :`(
}

// Now self is a strong reference
self.doSomething()

PS:私はいくつかの賛成票を持っているので、クロージャーのエスケープについての読書をお勧めします。

編集:@ tim-vermeulenがコメントしたように、Chris Lattnerは2016年1月22日金曜日19:51:29 CSTにこのトリックを使用しないでくださいと述べたので、使用しないでください。エスケープしないクロージャー情報と@gbkからのキャプチャリストの回答を確認してください。**

キャプチャリストで[weak self]を使用している場合は、selfがnilになる可能性があることに注意してください。最初に行うことは、ガードステートメントで確認することです

guard let `self` = self else {
   return
}
self.doSomething()

引用符の周りに何があるか疑問に思っている場合はself、名前をthisweakSelfなどに変更する必要なく、クロージャー内でselfを使用するためのプロトリックです。



2
ローカルの「セルフ」を「strongSelf」と呼び、デフォルトのセルフと混同しないようにし、強力なセルフリファレンスをガードしているかどうかを簡単に特定できるようにします。
ジャスティンスタンレー

1
これはコンパイラのバグなので、使用しないでください。lists.swift.org
Tim Vermeulen

1
上記のリンクにあるChris Lattnerのコメントは、変数にself(バッククォートで)という名前を付けていないだけだと思います。nonOptionalSelfなど、他の名前を付ければ問題ありません。
OutOnAWeekend 2017

1
最近(SWIFT 4.2){ [weak self] in guard let self = self else { return }バッククォートせずに使用することができ、実際にサポートされている:github.com/apple/swift-evolution/blob/master/proposals/...を
公園を。

26

キャプチャリストを使用

キャプチャリストの定義

キャプチャリストの各項目は、弱いまたは所有されていないキーワードと、クラスインスタンス(selfなど)または何らかの値で初期化された変数(delegate = self.delegate!など)への参照とのペアです。これらのペアは、コンマで区切られた1組の角括弧内に記述されます。

キャプチャリストをクロージャのパラメータリストの前に配置し、提供されている場合はタイプを返します。

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here 
} 

コンテキストから推測できるため、クロージャーがパラメーターリストまたは戻り値の型を指定しない場合、キャプチャーリストをクロージャーの先頭に配置し、その後にinキーワードを続けます。

lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // closure body goes here
}

追加説明


3
非所有を「自己」に使用したため、アクセスしたときに「自己」がnilにならないことが確実にわかります。次に、「self.delegate」に強制アンラップを使用して(これもnilにならないことが確実であることを意味します)、それを弱い変数に割り当てます。「self.delegate」がnilにならないことが確実にわかっている場合は、「delegate」で所有されていないものを弱い代わりに使用しないでください。
Roni Leshes 2017年

26

編集:LightManによる更新されたソリューションへの参照

LightManのソリューションを参照してください。今まで私は使っていました:

input.action = { [weak self] value in
    guard let this = self else { return }
    this.someCall(value) // 'this' isn't nil
}

または:

input.action = { [weak self] value in
    self?.someCall(value) // call is done if self isn't nil
}

通常、推測された場合、パラメーターの型を指定する必要はありません。

パラメータがない場合、または$0クロージャ内で参照する場合は、パラメータを完全に省略できます。

input.action = { [weak self] in
    self?.someCall($0) // call is done if self isn't nil
}

完全を期すために; クロージャを関数に渡していて、パラメータがでない@escaping場合、は必要ありませんweak self

[1,2,3,4,5].forEach { self.someCall($0) }

9

Swift 4.2以降、次のことが可能です。

_ = { [weak self] value in
    guard let self = self else { return }
    print(self) //👈 will never be nil
}()

他にも同様のソリューションがありますが、「これ」はC ++ IMHOです。「strongSelf」はAppleの慣例であり、あなたのコードを見た人は誰でも何が起こっているかを知っています。
デビッドH

1
@ David H IMOこのフレーズstrongSelfは変数の意味/副作用を明示的に説明しており、コードがより長い性質のものである場合に適しています。ただし、c ++がこのような表現を使用していることを知りませんでした。
eonist

3
スウィフト4.2の時点で、あなたは使用することができguard let self = self else { return }アンラップへ[weak self]github.com/apple/swift-evolution/blob/master/proposals/...
アメールHukic

@AmerHukic👌
eonist


3

ブロックのパラメーターの前のキャプチャリストで[weak self]または[unowned self]を使用できます。キャプチャリストはオプションの構文です。

[unowned self]セルがnilになることはないため、ここではうまく機能します。それ以外の場合は使用できます[weak self]


1
セルは自己ではない、彼はセルクラスではない、彼はおそらくビューコントローラーにいる...
フアンボエロ

0

おそらく必要以上にクラッシュしている場合[弱い自己]

あなたが作成しているブロックはどういうわけかまだ配線されていると思います。

prepareForReuseを作成し、その中のonTextViewEditClosureブロックをクリアしてみてください。

func prepareForResuse() {
   onTextViewEditClosure = nil
   textView.delegate = nil
}

それがクラッシュを防ぐかどうかを確認します。(それは単なる推測です)。


0

閉鎖と強い参照サイクル[概要]

ご存知のとおり、Swiftのクロージャーはインスタンスをキャプチャできます。これはself、クロージャー内で使用できることを意味します。特にescaping closure[About]strong reference cyclewhich を作成できます。ちなみに明示的にself内部で使用する必要がありescaping closureます。

SwiftクロージャーにはCapture List、キャプチャーされたインスタンスへの強い参照がないため、このような状況を回避して参照サイクルを中断できる機能があります。キャプチャリスト要素は、weak/ unownedと、クラスまたは変数への参照のペアです。

例えば

class A {
    private var completionHandler: (() -> Void)!
    private var completionHandler2: ((String) -> Bool)!

    func nonescapingClosure(completionHandler: () -> Void) {
        print("Hello World")
    }

    func escapingClosure(completionHandler: @escaping () -> Void) {
        self.completionHandler = completionHandler
    }

    func escapingClosureWithPArameter(completionHandler: @escaping (String) -> Bool) {
        self.completionHandler2 = completionHandler
    }
}

class B {
    var variable = "Var"

    func foo() {
        let a = A()

        //nonescapingClosure
        a.nonescapingClosure {
            variable = "nonescapingClosure"
        }

        //escapingClosure
        //strong reference cycle
        a.escapingClosure {
            self.variable = "escapingClosure"
        }

        //Capture List - [weak self]
        a.escapingClosure {[weak self] in
            self?.variable = "escapingClosure"
        }

        //Capture List - [unowned self]
        a.escapingClosure {[unowned self] in
            self.variable = "escapingClosure"
        }

        //escapingClosureWithPArameter
        a.escapingClosureWithPArameter { [weak self] (str) -> Bool in
            self?.variable = "escapingClosureWithPArameter"
            return true
        }
    }
}
  • weak-できれば、可能であれば使用してください
  • unowned -インスタンス所有者のライフタイムがクロージャよりも大きいことが確実な場合に使用します
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.