Apa perbedaan antara referensi yang lemah dan referensi yang tidak dimiliki?

240

Swift memiliki:

  • Referensi yang kuat
  • Referensi yang lemah
  • Referensi yang Tidak Dimiliki

Bagaimana perbedaan referensi yang tidak dimiliki berbeda dengan referensi yang lemah?

Kapan aman menggunakan referensi yang tidak dimiliki?

Apakah referensi yang tidak diketahui memiliki risiko keamanan seperti pointer yang menggantung di C / C ++?

Ian Ringrose
sumber
3
Artikel yang sangat bagus di andrewcbancroft.com/2015/05/05/...
Zeeshan
Pengalaman saya adalah menggunakan unowneduntuk kelas yang kami kontrol, untuk kelas Apple, gunakan weakkarena kami tidak dapat memastikan dengan pasti apa yang dilakukannya
onmyway133
@NoorAli, atau "dimiliki oleh" sebagai referensi "tidak dimiliki" sering menunjuk ke pemilik.
Ian Ringrose
1
CATATAN: Ada implikasi kinerja yang penting untuk diperhatikan dengan masing-masing referensi ini: stackoverflow.com/questions/58635303/…
Epic Byte
@ EpicByte Terkadang GC penuh seperti Java atau C # bernilai overhead.
Ian Ringrose

Jawaban:

361

Keduanya weakdan unownedreferensi tidak membuat strongpenangguhan pada objek yang dirujuk (alias mereka tidak menambah jumlah retain untuk mencegah ARC dari deallocating objek yang dirujuk).

Tapi mengapa dua kata kunci? Perbedaan ini berkaitan dengan fakta bahwa Optionaltipe adalah bawaan dalam bahasa Swift. Singkat cerita tentang mereka: tipe opsional menawarkan keamanan memori (ini bekerja dengan baik dengan aturan konstruktor Swift - yang ketat untuk memberikan manfaat ini).

Sebuah weakreferensi memungkinkan kemungkinan untuk menjadi nil(ini terjadi secara otomatis ketika objek direferensikan deallocated), oleh karena itu jenis properti Anda harus opsional - sehingga Anda, sebagai programmer, wajib untuk memeriksa sebelum Anda menggunakannya (pada dasarnya kompiler memaksa Anda, sebanyak mungkin, untuk menulis kode aman).

Sebuah unownedmengandaikan referensi bahwa itu tidak akan pernah menjadi nilselama masa pakai baterai. Referensi yang tidak dimiliki harus ditetapkan selama inisialisasi - ini berarti bahwa referensi akan didefinisikan sebagai jenis non-opsional yang dapat digunakan dengan aman tanpa pemeriksaan. Jika entah bagaimana objek yang dirujuk tidak dapat dialokasikan, maka aplikasi akan macet ketika referensi yang tidak dimiliki akan digunakan.

Dari dokumen Apple :

Gunakan referensi yang lemah kapan pun valid untuk referensi itu menjadi nol pada beberapa titik selama masa pakainya. Sebaliknya, gunakan referensi yang tidak dimiliki ketika Anda tahu bahwa referensi tersebut tidak akan pernah menjadi nol setelah ditetapkan selama inisialisasi.

Dalam dokumen, ada beberapa contoh yang membahas mempertahankan siklus dan cara memutusnya. Semua contoh ini diekstraksi dari dokumen .

Contoh weakkata kunci:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var tenant: Person?
}

Dan sekarang, untuk beberapa seni ASCII (Anda harus melihat dokumen - mereka memiliki diagram cantik):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

Contoh Persondan Apartmentmenunjukkan situasi di mana dua properti, keduanya diizinkan nol, memiliki potensi untuk menyebabkan siklus referensi yang kuat. Skenario ini paling baik diselesaikan dengan referensi yang lemah. Kedua entitas dapat eksis tanpa memiliki ketergantungan yang ketat pada yang lain.

Contoh unownedkata kunci:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}

Dalam contoh ini, a Customermungkin atau mungkin tidak memiliki CreditCard, tetapi a CreditCard akan selalu dikaitkan dengan a Customer. Untuk mewakili ini, Customerkelas memiliki cardproperti opsional , tetapi CreditCardkelas memiliki properti non-opsional (dan tidak dimiliki) customer.

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

Contoh Customerdan CreditCardmemperlihatkan situasi di mana satu properti yang diizinkan menjadi nol dan properti lain yang tidak dapat nol memiliki potensi untuk menyebabkan siklus referensi yang kuat. Skenario ini paling baik diselesaikan dengan referensi yang tidak dimiliki.

Catatan dari Apple:

Referensi yang lemah harus dinyatakan sebagai variabel, untuk menunjukkan bahwa nilainya dapat berubah saat runtime. Referensi yang lemah tidak dapat dinyatakan sebagai konstanta.

Ada juga skenario ketiga ketika kedua properti harus selalu memiliki nilai, dan properti tidak boleh nihil setelah inisialisasi selesai.

Dan ada juga skenario siklus mempertahankan klasik yang harus dihindari ketika bekerja dengan penutupan.

Untuk ini, saya mendorong Anda untuk mengunjungi dokumen Apple , atau membaca buku .

Ilea Cristian
sumber
3
Ini agak sepele, tetapi saya menemukan contoh Apartemen dan Orang agak membingungkan yang juga menghadirkan solusi tambahan untuk memutus siklus referensi yang kuat. Apartemen seseorang adalah opsional dan karena itu bisa nol serta penyewa apartemen adalah opsional dan karena itu bisa nol sehingga kedua properti dapat didefinisikan sebagai lemah. `` `
Justin Levi Winter
class Person {let name: String init (name: String) {self.name = name} lemah var apartment: Apartment? } class Apartment {let number: Int init (number: Int) {self.number = number} lemah var tenant: Orang? }
Justin Levi Winter
3
Apa perbedaan antara weak var Person?vs var Person??
Dekan
4
@JustinLevi, Jika Anda mendeklarasikan kedua properti sebagai lemah, ada kemungkinan bagi mereka untuk tidak dialokasikan. Orang menyimpan referensi yang kuat ke Apartemen sehingga Apartemen tidak akan dialokasikan. Jika apartemen akan memiliki referensi kuat yang sama terhadap Orang, mereka akan membuat siklus penahan - yang dapat dipecah oleh programmer saat runtime jika dia tahu tentang hal itu, tetapi selain itu hanya kebocoran memori. Ini semua tentang masalah kuat, lemah dan tidak dimiliki: manajemen memori pada tingkat yang lebih tinggi, karena ARC melakukan semua hal kotor untuk kita. Menghindari mempertahankan siklus adalah tugas kita.
Ilea Cristian
1
Apakah satu-satunya manfaat dari tidak dimiliki atas yang lemah adalah bahwa Anda tidak perlu membuka dan dapat menggunakan konstanta? Apakah ada contoh di mana Anda tidak bisa menggunakan yang lemah dan hanya bisa menggunakan yang tidak dimiliki?
Alan
29

Q1. Apa perbedaan antara “Referensi yang tidak dimiliki” dengan “Referensi yang Lemah”?

Referensi yang lemah:

Referensi yang lemah adalah referensi yang tidak memiliki kekuatan yang kuat pada instance yang dirujuk, dan karenanya tidak menghentikan ARC untuk membuang instance yang direferensikan. Karena referensi yang lemah diizinkan memiliki “tidak ada nilai”, Anda harus menyatakan setiap referensi yang lemah memiliki jenis opsional. (Apple Documents)

Referensi Tidak Dimiliki:

Seperti referensi yang lemah, referensi yang tidak dimiliki tidak memiliki kekuatan yang kuat pada contoh yang dirujuk. Tidak seperti referensi yang lemah, bagaimanapun, referensi yang tidak dimiliki diasumsikan selalu memiliki nilai. Karena itu, referensi yang tidak dimiliki selalu didefinisikan sebagai tipe non-opsional. (Apple Documents)

Kapan Harus Menggunakan Masing-masing:

Gunakan referensi yang lemah kapan pun valid untuk referensi itu menjadi nol pada beberapa titik selama masa pakainya. Sebaliknya, gunakan referensi yang tidak dimiliki ketika Anda tahu bahwa referensi tersebut tidak akan pernah menjadi nol setelah ditetapkan selama inisialisasi. (Apple Documents)


Q2. Kapan aman menggunakan "referensi tidak dimiliki"?

Seperti dikutip di atas, referensi yang tidak dimiliki diasumsikan selalu memiliki nilai. Jadi, Anda hanya boleh menggunakannya ketika Anda yakin bahwa referensi tidak akan pernah nol. Apple Documents mengilustrasikan kasus penggunaan untuk referensi yang tidak dimiliki melalui contoh berikut.

Misalkan kita memiliki dua kelas Customerdan CreditCard. Pelanggan dapat ada tanpa kartu kredit, tetapi kartu kredit tidak akan ada tanpa pelanggan, yaitu dapat diasumsikan bahwa kartu kredit akan selalu memiliki pelanggan. Jadi, mereka harus memiliki hubungan sebagai berikut:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3. Apakah "referensi yang tidak dimiliki" merujuk risiko keamanan seperti "pointer menggantung" di C / C ++

Saya kira tidak.

Karena referensi yang tidak dimiliki hanyalah referensi lemah yang dijamin memiliki nilai, itu tidak boleh menjadi risiko keamanan dengan cara apa pun. Namun, jika Anda mencoba mengakses referensi yang tidak dimiliki setelah instansinya dibatalkan, Anda akan memicu kesalahan runtime, dan aplikasi akan macet.

Itulah satu-satunya risiko yang saya lihat.

Tautan ke Apple Documents

Myxtic
sumber
contoh program Q2 Anda mudah dimengerti tentang tidak dimiliki..terimakasih .. dapatkah Anda menambahkan jenis contoh yang sama untuk yang lemah & kuat ..
Ranjith Kumar
Luar biasa. Terima kasih.
Swifty McSwifterton
Bisakah Anda memasukkan contoh umum untuk tidak dimiliki atau lemah?
Sayang
Pertimbangkan objek orangtua & anak, jika anak tidak dapat ada tanpa orangtua maka gunakan unowneduntuk properti orangtua di kelas anak. lemah adalah sebaliknya. Penjelasan bagus @myxtic! unownedreferensi hanya weakreferensi yang dijamin memiliki nilai!
Saif
26

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 tidak dimiliki] maka diri mungkin nihil di beberapa titik dalam penutupan itu dan Anda mungkin perlu menggunakan [diri lemah] sebagai gantinya.

Lihatlah contoh penggunaan kuat , lemah , dan tidak dimiliki dalam penutupan:

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html

TenaciousJay
sumber
7
Mengapa tidak menggunakan saja yang lemah bahkan jika diri tidak pernah nol, tidak ada salahnya dilakukan dengan benar?
Boon
4
hai @Boon - itu memang pertanyaan kritis.
Fattie
[lemah diri] => Jika saya menggunakan closure inside viewDidLoad (), bagaimana bisa selfnihil?
Hassan Tareq
@HassanTareq, saya pikir beberapa contoh yang baik disebutkan dalam artikel, yang disebutkan di atas. Periksa bagian "Menyelesaikan Siklus Referensi yang Kuat untuk Penutupan", khususnya. Quote: "Swift mengharuskan Anda untuk menulis self.someProperty atau self.someMethod () (bukan hanya SomeProperty atau someMethod ()) setiap kali Anda merujuk ke anggota diri dalam penutupan. Ini membantu Anda mengingat bahwa dimungkinkan untuk menangkap diri sendiri dengan kecelakaan." Kutipan dari: Apple Inc. “Bahasa Pemrograman Swift (Swift 4).” iBooks. itunes.apple.com/de/book/the-swift-programming-language-swift-4/… "
Nick Entin
1
@ Karun Jika Anda selalu menggunakan lemah, kompiler akan dipaksa untuk memeriksa opsi sebelum digunakan. Jika Anda tidak memberi centang, itu akan memberikan kesalahan waktu kompilasi. Tidak ada salahnya.
Vikas Mishra
5

Ekstrak dari tautan

Beberapa poin penutup

  • Untuk menentukan apakah Anda perlu khawatir tentang kuat, lemah, atau tidak dimiliki, tanyakan, "Apakah saya berurusan dengan jenis referensi". Jika Anda bekerja dengan Structs atau Enums, ARC tidak mengelola memori untuk Tipe-tipe itu dan Anda bahkan tidak perlu khawatir tentang menentukan yang lemah atau tidak dimiliki untuk konstanta atau variabel tersebut.
  • Referensi yang kuat baik-baik saja dalam hubungan hierarkis di mana orang tua mereferensikan anak, tetapi tidak sebaliknya. Faktanya, referensi kuat adalah jenis referensi yang paling tepat untuk sebagian besar waktu.
  • Ketika dua instance secara opsional terkait satu sama lain, pastikan bahwa salah satu instance memiliki referensi yang lemah satu sama lain.
  • Ketika dua instance terkait sedemikian rupa sehingga salah satu instance tidak dapat ada tanpa yang lain, instance dengan dependensi wajib harus memiliki referensi yang tidak dimiliki ke instance lain.
Abhinav Singh
sumber
1

Keduanya weakdan unownedreferensi tidak akan memengaruhi jumlah referensi objek. Tetapi referensi yang lemah akan selalu bersifat opsional yaitu bisa nol, sedangkan unownedreferensi tidak pernah bisa nol sehingga mereka tidak akan pernah opsional. Saat menggunakan referensi opsional, Anda harus selalu menangani kemungkinan objek menjadi nol. Dalam hal referensi yang tidak dimiliki, Anda harus memastikan bahwa objek tidak pernah nol. Menggunakan referensi yang tidak dimiliki untuk objek nil akan sama dengan membuka secara paksa opsi yang nil.

Yang mengatakan itu aman untuk menggunakan referensi yang tidak dimiliki di mana Anda yakin bahwa umur objek lebih dari referensi. Jika bukan itu masalahnya, lebih baik menggunakan referensi yang lemah sebagai gantinya.

Adapun bagian ketiga dari pertanyaan, saya tidak berpikir referensi yang tidak dimiliki mirip dengan pointer menggantung. Ketika kita berbicara tentang jumlah referensi, kita biasanya merujuk pada jumlah referensi yang kuat dari objek. Demikian pula swift mempertahankan jumlah referensi yang tidak dimiliki dan jumlah referensi yang lemah untuk objek (referensi lemah menunjuk ke sesuatu yang disebut "tabel samping" daripada objek itu sendiri). Ketika jumlah referensi yang kuat mencapai nol, objek akan diinisialisasi, tetapi itu tidak dapat dibatalkan alokasinya jika jumlah referensi yang tidak dimiliki lebih dari nol.

Sekarang pointer menggantung adalah sesuatu yang menunjuk ke lokasi memori yang telah di-deallocated. Tetapi dengan cepat karena memori hanya dapat dideallocating selama ada referensi yang tidak dimiliki objek, itu tidak dapat menyebabkan pointer menggantung.

Ada banyak artikel yang membahas manajemen memori cepat secara lebih rinci. Ini satu.

Deeksha Kaul
sumber
0

Referensi yang tidak dimiliki adalah sejenis referensi lemah yang digunakan dalam kasus hubungan Same-Seumur Hidup antara dua objek, ketika sebuah objek seharusnya hanya dimiliki oleh satu objek lain. Ini adalah cara untuk membuat pengikatan abadi antara objek dan salah satu propertinya.

Dalam contoh yang diberikan dalam video WWDC cepat antara, seseorang memiliki kartu kredit, dan kartu kredit hanya dapat memiliki satu pemegang. Pada kartu kredit, orang tersebut tidak boleh menjadi properti opsional, karena Anda tidak ingin memiliki kartu kredit yang beredar hanya dengan satu pemilik. Anda dapat memutus siklus ini dengan menjadikan properti pemegang kredit sebagai referensi lemah, tetapi itu juga mengharuskan Anda untuk menjadikannya opsional dan juga variabel (tidak seperti konstanta). Referensi yang tidak diketahui dalam hal ini berarti bahwa meskipun CreditCard tidak memiliki kepemilikan saham pada Seseorang, nyawanya tergantung padanya.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}
JuJoDi
sumber
tautan ke video atau judul wwdc?
Osa
-2

Gunakan unownedsaat Anda yakin selftidak akan pernah bisa nilsampai di titik yang Anda akses selfpada saat itu.

Contoh (Anda tentu saja dapat menambahkan target langsung dari MyViewController, tetapi sekali lagi, ini adalah contoh sederhana) .:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myButton = MyButton { [unowned self] in
            print("At this point, self can NEVER be nil. You are safe to use unowned.")
            print("This is because myButton can not be referenced without/outside this instance (myViewController)")
        }
    }
}

class MyButton: UIButton {
    var clicked: (() -> ())

    init(clicked: (() -> ())) {
        self.clicked = clicked

        // We use constraints to layout the view. We don't explicitly set the frame.
        super.init(frame: .zero)

        addTarget(self, action: #selector(clicked), for: .touchUpInside)
    }

    @objc private func sendClosure() {
        clicked()
    }
}

Gunakan weaksaat ada kemungkinan selfbisa nilpada titik yang Anda akses self.

Contoh:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
            print("Can you guarentee that self is always available when the network manager received data?")
            print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
            print("You should use weak self here, since you are not sure if this instance is still alive for every")
            print("future callback of network manager")
        }
    }
}

class NetworkManager {

    static let sharedInstance = NetworkManager()

    var receivedData: ((Data) -> ())?

    private func process(_ data: Data) {
        // process the data...

        // ... eventually notify a possible listener.
        receivedData?(data)
    }
}

Kekurangan dari unowned:

  • Lebih efisien daripada lemah
  • Anda dapat (yah, Anda dipaksa) untuk menandai instance sebagai immutable (tidak lagi sejak Swift 5.0).
  • Menunjukkan pembaca kode Anda: Contoh ini memiliki hubungan dengan X dan tidak dapat hidup tanpanya, tetapi jika X hilang, saya juga pergi.

Kekurangan dari weak:

  • Lebih aman daripada tidak dimiliki (karena tidak bisa crash).
  • Dapat membuat hubungan dengan X yang berjalan di kedua cara, tetapi keduanya dapat hidup tanpa satu sama lain.

Jika Anda tidak yakin, gunakan weak. Tunggu , maksud saya tanyakan di sini di StackOverflow apa yang harus Anda lakukan dalam kasus Anda! Menggunakan lemah setiap saat ketika Anda seharusnya tidak hanya membingungkan bagi Anda dan pembaca kode Anda.

J. Doe
sumber