Apakah Swift Pass By Value atau Pass By Reference

100

Saya benar-benar baru mengenal Swift dan saya baru saja membaca bahwa kelas diteruskan oleh referensi dan array / string dll disalin.

Apakah cara lulus dengan referensi sama seperti di Objective-C atau Java di mana Anda benar-benar meneruskan referensi "a" atau apakah itu benar lewat referensi?

gran_profaci.dll
sumber
"Apakah pass by reference sama seperti di Objective-C atau Java" Baik Objective-C maupun Java tidak memiliki pass-by-reference.
newacct
2
Iya. Saya tahu itu. Anda tidak melewatkan referensi. Anda melewatkan referensi dengan nilai. Saya berasumsi bahwa itu diketahui saat menjawab.
gran_profaci
Java meneruskan nilai, bukan referensi.
6rchid

Jawaban:

167

Jenis Hal di Swift

Aturannya adalah:

  • Instance kelas adalah tipe referensi (yaitu referensi Anda ke instance kelas secara efektif adalah pointer )

  • Fungsi adalah tipe referensi

  • Yang lainnya adalah tipe nilai ; "yang lainnya" berarti contoh dari struct dan contoh enum, karena hanya itu yang ada di Swift. Array dan string adalah instance struct, misalnya. Anda bisa meneruskan referensi ke salah satu dari hal itu (sebagai argumen fungsi) dengan menggunakan inoutdan mengambil alamat, seperti yang ditunjukkan oleh newacct. Tapi tipe itu sendiri adalah tipe nilai.

Apa Arti Jenis Referensi Bagi Anda

Objek tipe referensi dalam praktiknya khusus karena:

  • Penugasan belaka atau meneruskan ke fungsi dapat menghasilkan banyak referensi ke objek yang sama

  • Objek itu sendiri bisa berubah meskipun referensinya adalah konstanta ( let, baik eksplisit maupun tersirat).

  • Mutasi objek memengaruhi objek itu seperti yang terlihat oleh semua referensi padanya.

Itu bisa berbahaya, jadi waspadalah. Di sisi lain, meneruskan tipe referensi jelas efisien karena hanya sebuah pointer yang disalin dan diteruskan, yang sepele.

Apa Arti Jenis Nilai Bagi Anda

Jelas, meneruskan tipe nilai adalah "lebih aman", dan letberarti seperti yang dikatakan: Anda tidak dapat mengubah instance struct atau instance enum melalui letreferensi. Di sisi lain, keamanan itu dicapai dengan membuat salinan nilai yang terpisah, bukan? Bukankah itu membuat pengoperan jenis nilai berpotensi mahal?

Ya dan tidak. Ini tidak seburuk yang Anda kira. Seperti yang dikatakan Nate Cook, meneruskan tipe nilai tidak selalu berarti menyalin, karena let(eksplisit atau tersirat) menjamin keabadian sehingga tidak perlu menyalin apa pun. Dan bahkan melewati menjadi varreferensi tidak berarti bahwa hal-hal akan disalin, hanya bahwa mereka dapat jika diperlukan (karena ada mutasi). Para dokter secara khusus menyarankan Anda untuk tidak membengkokkan celana dalam.

Matt
sumber
6
"Instance kelas diteruskan dengan referensi. Fungsi diteruskan dengan referensi" Tidak. Ini adalah nilai lewat ketika parameter tidak inoutterlepas dari tipenya. Apakah sesuatu itu lewat-demi-referensi adalah ortogonal terhadap tipe.
newacct
4
@newacct Nah, tentu saja Anda benar dalam arti yang sebenarnya! Secara tegas, seseorang harus mengatakan bahwa semuanya pass-by-value tetapi instance enum dan instance struct adalah tipe nilai dan instance class serta fungsi adalah tipe referensi . Lihat, misalnya, developer.apple.com/swift/blog/?id=10 - Juga lihat developer.apple.com/library/ios/documentation/Swift/Conceptual/… Namun, saya pikir apa yang saya katakan sesuai dengan arti kata-kata itu berarti.
matt
6
Benar, dan tipe nilai / tipe referensi tidak boleh disamakan dengan pass-by-value / pass-by-reference, karena tipe nilai dapat diteruskan oleh nilai atau referensi, dan tipe referensi juga dapat diteruskan oleh nilai atau referensi.
newacct
1
@newct diskusi yang sangat berguna; Saya menulis ulang ringkasan saya agar tidak menyesatkan.
matt
43

Itu selalu nilai lewat jika parameternya tidak inout.

Itu selalu merupakan referensi lewat jika parameternya adalah inout. Namun, ini agak rumit karena Anda perlu menggunakan &operator secara eksplisit pada argumen saat meneruskan ke inoutparameter, jadi mungkin tidak sesuai dengan definisi tradisional pass-by-reference, di mana Anda meneruskan variabel secara langsung.

newacct
sumber
3
Jawaban ini, digabungkan dengan Nate Cook's, menjadi lebih jelas bagi saya (berasal dari C ++) seputar fakta bahwa bahkan "tipe referensi" tidak akan dimodifikasi di luar cakupan fungsi kecuali Anda secara eksplisit menentukannya (dengan menggunakan inout)
Gobe
10
inoutsebenarnya tidak lewat referensi tapi copy-in copy-out Ini hanya menjamin bahwa nilai setelah panggilan fungsi diubah akan ditugaskan ke argumen asli. Parameter Masuk-Keluar
Dalija Prasnikar
meskipun benar bahwa semuanya adalah nilai yang lewat. Properti tipe referensi dapat dimodifikasi di dalam fungsi sebagai referensi salinan ke instance yang sama.
MrAn3
42

Segala sesuatu di Swift diteruskan oleh "copy" secara default, jadi ketika Anda meneruskan tipe-nilai Anda mendapatkan salinan dari nilai tersebut, dan ketika Anda mengirimkan tipe referensi Anda mendapatkan salinan referensi, dengan semua yang tersirat. (Artinya, salinan referensi masih menunjuk ke contoh yang sama dengan referensi asli.)

Saya menggunakan kutipan menakutkan di sekitar "salinan" di atas karena Swift melakukan banyak pengoptimalan; sedapat mungkin, itu tidak menyalin sampai ada mutasi atau kemungkinan mutasi. Karena parameter tidak dapat diubah secara default, ini berarti seringkali tidak ada salinan yang benar-benar terjadi.

Nate Cook
sumber
Bagi saya ini adalah jawaban terbaik karena menjelaskan bahwa misalnya properti contoh dapat dimodifikasi di dalam fungsi bahkan parameternya adalah salinan (nilai lewat) karena menunjuk ke referensi yang sama.
MrAn3
9

Berikut adalah contoh kode kecil untuk melewati referensi. Hindari melakukan ini, kecuali Anda punya alasan kuat.

func ComputeSomeValues(_ value1: inout String, _ value2: inout Int){
    value1 = "my great computation 1";
    value2 = 123456;
}

Sebut saja seperti ini

var val1: String = "";
var val2: Int = -1;
ComputeSomeValues(&val1, &val2);
Chris Amelinckx
sumber
Mengapa Anda harus menghindari melakukan ini?
Berotak
1
@Brainless karena menambahkan kerumitan yang tidak perlu ke kode. Yang terbaik adalah memasukkan parameter dan mengembalikan satu hasil. Harus melakukan ini, biasanya menandakan desain yang buruk. Cara lain untuk menjelaskannya adalah bahwa efek samping tersembunyi dalam variabel referensi yang diteruskan tidak transparan bagi pemanggil.
Chris Amelinckx
Ini tidak lewat referensi. inoutadalah salinan masuk, salinan keluar. Ini pertama-tama akan menyalin di objek, lalu menimpa objek asli setelah fungsi kembali. Meskipun kelihatannya sama, ada perbedaan yang tidak kentara.
Hannes Hertach
7

The Apel Swift Developer blog memiliki posting yang disebut Nilai dan Referensi Jenis yang menyediakan diskusi yang jelas dan rinci tentang topik ini sangat.

Kutipan:

Jenis di Swift termasuk dalam salah satu dari dua kategori: pertama, "tipe nilai", di mana setiap instance menyimpan salinan unik datanya, biasanya didefinisikan sebagai struct, enum, atau tuple. Kedua, "jenis referensi", di mana instance berbagi satu salinan data, dan jenisnya biasanya didefinisikan sebagai kelas.

Posting blog Swift terus menjelaskan perbedaan dengan contoh dan menyarankan kapan Anda akan menggunakan salah satunya.

mengapaceewhite
sumber
1
Ini tidak menjawab pertanyaan itu. Pertanyaannya adalah tentang pass-by-value vs. pass-by-reference yang sepenuhnya ortogonal terhadap tipe nilai vs. tipe referensi.
Jörg W Mittag
2

Kelas dilewatkan oleh referensi dan yang lain diteruskan dengan nilai secara default. Anda dapat menyampaikan referensi dengan menggunakan inoutkata kunci.

Bahram Zangene
sumber
Ini salah inoutadalah salinan masuk, salinan keluar. Ini pertama-tama akan menyalin di objek, lalu menimpa objek asli setelah fungsi kembali. Meskipun kelihatannya sama, ada perbedaan yang tidak kentara.
Hannes Hertach
2

Jika Anda menggunakan inout dengan operator infix seperti + = maka simbol & alamat dapat diabaikan. Saya kira kompilator mengasumsikan lewat referensi?

extension Dictionary {
    static func += (left: inout Dictionary, right: Dictionary) {
        for (key, value) in right {
            left[key] = value
        }
    }
}

origDictionary + = newDictionaryToAdd

Dan bagusnya kamus ini 'tambah' hanya melakukan satu tulisan ke referensi aslinya juga, sangat bagus untuk mengunci!

Jules Burt
sumber
2

Kelas dan struktur

Salah satu perbedaan terpenting antara struktur dan kelas adalah bahwa struktur selalu disalin ketika diteruskan dalam kode Anda, tetapi kelas diteruskan oleh referensi.

Penutupan

Jika Anda menetapkan closure ke properti instance kelas, dan closure tersebut menangkap instance itu dengan merujuk ke instance atau anggotanya, Anda akan membuat siklus referensi yang kuat antara closure dan instance tersebut. Swift menggunakan daftar tangkapan untuk memutus siklus referensi yang kuat ini

ARC (Penghitungan Referensi Otomatis)

Penghitungan referensi hanya berlaku untuk instance kelas. Struktur dan enumerasi adalah tipe nilai, bukan tipe referensi, dan tidak disimpan dan diteruskan oleh referensi.

mohsen
sumber