Cara Benar Menangani Lemah di Blok Swift dengan Argumen

151

Di saya TextViewTableViewCell, saya punya variabel untuk melacak blok dan metode konfigurasi tempat blok dilewatkan dan ditugaskan.
Ini TextViewTableViewCellkelas saya :

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

Ketika saya menggunakan metode configure dalam metode saya cellForRowAtIndexPath, bagaimana cara menggunakan diri lemah di blok yang saya lewati.
Berikut adalah apa yang saya miliki tanpa diri lemah:

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

PEMBARUAN : Saya mendapat yang berikut untuk bekerja menggunakan [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

Ketika saya melakukan dan [unowned self]bukannya [weak self]mengeluarkan ifpernyataan itu, aplikasi mogok. Ada ide tentang bagaimana ini harus bekerja [unowned self]?

NatashaTheRobot
sumber
Bisakah Anda memilih jawaban di bawah ini sebagai jawaban yang benar? Juga perhatikan bahwa dengan tidak memiliki kepemilikan Anda tidak perlu memperkuat diri dalam penutupan Anda. Tidak dimiliki lebih baik daripada lemah di sini karena siklus hidup sel dan pengontrol tampilan Anda terhubung.
ikuramedia
1
Saya menyadari bahwa [tidak dimiliki sendiri] jika opsi yang lebih baik, tetapi aplikasi saya crash ketika saya menggunakannya. Senang melihat contoh kode menggunakannya untuk menutup jawabannya.
NatashaTheRobot
1
Dari dokumen: "Seperti referensi yang lemah, referensi yang tidak dimiliki tidak memiliki pegangan yang kuat pada contoh yang dimaksud. Namun, tidak seperti referensi yang lemah, referensi yang tidak dimiliki diasumsikan selalu memiliki nilai." Jika aplikasi Anda mogok, itu mungkin karena tidak dimiliki sedang diterapkan ke nilai yang nol saat runtime.
Bill Patterson
Mungkin lebih baik untuk mengiklankan pernyataan penjaga di sini daripada jika membiarkan mengikat diri kuat. Hanya mengatakan, ini seperti kandidat yang sempurna :-D
Daniel Galasko
@ NatashaTheRobot, Sintaks apa itu [diri lemah]? terlihat seperti pesan yang lewat di objektif C. Tolong tambahkan sedikit tentang sintaks dalam pertanyaan.
Vignesh

Jawaban:

178

Jika diri bisa nol dalam penutupan gunakan [diri lemah] .

Jika diri tidak akan pernah nol dalam penutupan gunakan [diri yang tidak dimiliki] .

Jika crash ketika Anda menggunakan [diri yang tidak dimiliki] saya akan menebak bahwa diri adalah nol di beberapa titik dalam penutupan itu, itulah sebabnya Anda harus pergi dengan [diri yang lemah] sebagai gantinya.

Saya benar-benar menyukai seluruh bagian dari manual tentang penggunaan kuat , lemah , dan tidak dimiliki dalam penutupan:

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

Catatan: Saya menggunakan istilah penutupan alih-alih blok yang merupakan istilah Swift yang lebih baru:

Perbedaan antara blok (Objective C) dan closure (Swift) di ios

TenaciousJay
sumber
7
Apple menyebut blok "penutupan" dalam dokumen pertama mereka untuk ekstensi bahasa C. (Blok atau penutupan adalah perpanjangan dari C pada awalnya. Hanya MM yang terkait dengan Objective-C.) Bahkan saya lebih suka istilah "penutupan", juga, karena "blok" dalam C terkait dengan pernyataan majemuk sangat sering, itu adalah jenis yang salah dalam kedua bahasa, karena disebut penutupan bahkan tidak menutup objek (variabel atau konstan).
Amin Negm-Awad
1
dijawab dengan sangat baik :)
iDevAmit
1
Saya sarankan tidak pernah menggunakan unowned. Ini tidak sebanding dengan risiko menyebabkan aplikasi Anda mogok.
Kyle Redfearn
32

Masukkan [unowned self]sebelum (text: String)...di penutupan Anda. Ini disebut daftar penangkapan dan menempatkan instruksi kepemilikan pada simbol yang ditangkap di penutupan.

ikuramedia
sumber
2
Terima kasih telah menyebutkan namanya, saya ingin tahu itu!
rob5408
3
Saya kira jawaban ini tidak berguna. [diri yang tidak dimiliki] akan macet jika diri menjadi nol selama eksekusi penutupan
Yunus Nedim Mehel
3
sama sekali tidak ada alasan untuk menggunakan yang tidak dimiliki, selain (1) dalam situasi yang sangat tidak biasa, untuk kinerja (ini sama sekali tidak relevan di sini dan dalam 99,999% pemrograman) dan (2) sebagai masalah gaya-penegakan. Pernyataan "Anda harus selalu menggunakan yang lemah, tidak pernah tidak dimiliki" sangat masuk akal.
Fattie
29

** DIedit untuk Swift 4.2:

Seperti yang dikomentari @Koen, swift 4.2 memungkinkan:

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

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

PS: Karena saya mendapat suara, saya ingin merekomendasikan bacaan tentang lolos dari penutupan .

Diedit: Seperti yang dikomentari @ tim-vermeulen, Chris Lattner mengatakan pada Jumat 22 Januari 19:51:29 CST 2016, trik ini tidak boleh digunakan pada diri sendiri, jadi tolong jangan gunakan itu. Periksa info penutupan yang tidak lolos dan jawaban daftar tangkap dari @ gbk. **

Bagi mereka yang menggunakan [diri lemah] dalam daftar tangkap, perhatikan bahwa diri bisa nol, jadi hal pertama yang saya lakukan adalah memeriksa dengan pernyataan penjaga

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

Jika Anda bertanya-tanya apa tanda kutip ada di sekitar selfadalah trik pro untuk menggunakan diri di dalam penutupan tanpa perlu mengubah nama untuk ini , diri lemah atau apa pun.

LightMan
sumber
2
` diri`
Cullen SUN
2
Saya cenderung menyebut "diri" lokal "kuat" untuk memastikannya tidak bingung dengan diri default dan lebih mudah dikenali apakah Anda telah menjaga referensi diri yang kuat.
Justin Stanley
1
Ini tidak boleh digunakan, karena ini adalah bug penyusun: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/…
Tim Vermeulen
1
Saya pikir komentar oleh Chris Lattner di tautan di atas hanya tentang tidak menamai variabel sebagai self(di backticks). Beri nama itu sesuatu yang lain seperti NonOptionalSelf dan itu akan baik-baik saja.
OutOnAWeekend
1
Saat ini (swift 4.2) { [weak self] in guard let self = self else { return }dapat digunakan tanpa backticks, dan sebenarnya didukung: github.com/apple/swift-evolution/blob/master/proposals/…
Koen.
26

Gunakan daftar Tangkap

Menentukan Daftar Capture

Setiap item dalam daftar tangkap adalah pasangan kata kunci yang lemah atau tidak dimiliki dengan referensi ke instance kelas (seperti diri sendiri) atau variabel yang diinisialisasi dengan beberapa nilai (seperti delegate = self.delegate!). Pasangan ini ditulis dalam sepasang kawat gigi persegi, dipisahkan dengan koma.

Tempatkan daftar tangkap sebelum daftar parameter penutupan dan ketik kembali jika disediakan:

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

Jika penutupan tidak menentukan daftar parameter atau tipe pengembalian karena mereka dapat disimpulkan dari konteks, tempatkan daftar penangkapan di awal penutupan, diikuti dengan kata kunci in:

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

penjelasan tambahan

gbk
sumber
3
Anda menggunakan yang tidak dimiliki untuk "diri" yang berarti Anda tahu pasti "diri" tidak akan nol ketika Anda mengaksesnya. Kemudian Anda menggunakan paksa membuka "self.delegate" (yang juga berarti Anda tahu pasti tidak akan ada) untuk menetapkannya ke var lemah. Jika Anda tahu pasti bahwa "self.delegate" tidak akan menjadi nol, mengapa tidak menggunakan yang tidak dimiliki pada "delegate" bukannya lemah?
Roni Leshes
26

EDIT: Referensi ke solusi yang diperbarui oleh LightMan

Lihat solusi LightMan . Sampai sekarang saya menggunakan:

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

Atau:

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

Biasanya Anda tidak perlu menentukan tipe parameter jika disimpulkan.

Anda dapat menghilangkan parameter sama sekali jika tidak ada atau jika Anda merujuknya seperti $0pada penutupan:

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

Hanya untuk kelengkapan; jika Anda meneruskan penutupan ke suatu fungsi dan parameter tidak @escaping, Anda tidak perlu weak self:

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

Mulai dari 4.2 🔸 yang dapat kita lakukan:

_ = { [weak self] value in
    guard let self = self else { return }
    print(self) //👈 will never be nil
}()
eonist
sumber
Lainnya memiliki solusi yang serupa, tetapi "ini" adalah C ++ IMHO. "strongSelf" adalah konvensi Apple dan siapa pun yang melirik kode Anda akan tahu apa yang terjadi.
David H
1
@ David H IMO frasa ini strongSelfsecara eksplisit menjelaskan arti variabel / efek samping yang bagus jika kodenya lebih panjang. menghargai pendapat Anda, tidak tahu c ++ menggunakan ungkapan seperti itu.
eonist
3
Pada Swift 4.2 Anda dapat menggunakan guard let self = self else { return } untuk membuka [weak self]: github.com/apple/swift-evolution/blob/master/proposals/…
Amer Hukic
@AmerHukic 👌.
eonist
3

Anda dapat menggunakan [diri lemah] atau [diri tidak dimiliki] dalam daftar penangkapan sebelum parameter blok Anda. Daftar penangkapan adalah sintaks opsional.

[unowned self]bekerja dengan baik di sini karena sel tidak akan pernah menjadi nol. Kalau tidak, Anda bisa menggunakan[weak self]

Rufus
sumber
1
sel itu bukan diri, dia bukan di kelas sel, dia mungkin di viewcontroller ...
Juan Boero
0

Jika Anda menabrak dari yang Anda butuhkan [lemah diri]

Dugaan saya adalah bahwa blok yang Anda buat entah bagaimana masih terhubung.

Buat preparForReuse dan coba bersihkan blok onTextViewEditClosure di dalamnya.

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

Lihat apakah itu mencegah crash. (Itu hanya tebakan).

Michael Gray
sumber
0

Penutupan dan siklus referensi yang kuat [Tentang]

Seperti yang Anda tahu penutupan Swift dapat menangkap contoh. Ini berarti bahwa Anda dapat menggunakan selfdi dalam penutupan. Terutama escaping closure[Tentang] dapat membuat strong reference cycleapa. Ngomong-ngomong, Anda harus menggunakan selfdi dalam secara eksplisitescaping closure .

Penutupan Swift memiliki Capture Listfitur yang memungkinkan Anda menghindari situasi seperti itu dan memutus siklus referensi karena tidak memiliki referensi yang kuat untuk instance yang ditangkap. Elemen Capture List adalah sepasang weak/unowned dan referensi ke kelas atau variabel.

Sebagai contoh

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- lebih disukai, gunakan jika memungkinkan
  • unowned - gunakan ketika Anda yakin bahwa pemilik instance seumur hidup lebih besar dari penutupan
yoAlex5
sumber