Değere Göre Swift mi yoksa Referansla mı Geçer?


100

Swift konusunda gerçekten yeniyim ve sadece sınıfların referansla aktarıldığını ve dizilerin / dizelerin vb. Kopyalandığını okudum.

Referansla geçiş, aslında "bir" referansı ilettiğiniz Objective-C veya Java'dakiyle aynı şekilde mi yoksa referansla uygun geçiş mi?


"Referansla geçiş, Objective-C veya Java'dakiyle aynı şekilde mi?" Objective-C veya Java'da referansla geçiş özelliği yoktur.
newacct

2
Evet. Bunu biliyorum. Referans ile geçemezsiniz. Referansı değere göre iletirsiniz. Cevap verirken bunun bilindiğini varsaydım.
gran_profaci

Java referansa göre değil değere göre geçer.
6rchid

Yanıtlar:


167

Swift sınırlarındaki Şey Türleri

Kural şudur:

  • Sınıf örnekleri başvuru türleridir (yani , bir sınıf örneğine başvurunuz etkili bir şekilde bir göstericidir )

  • Fonksiyonlar referans türleridir

  • Diğer her şey bir değer türüdür ; "diğer her şey" kısaca yapı örnekleri ve numaralandırma örnekleri anlamına gelir, çünkü Swift'de tüm olan budur. Örneğin, diziler ve dizeler yapı örnekleridir. Sen edebilirsiniz kullanarak (bir işlev bağımsız değişken olarak) bunlardan birinin başvuru geçmesi inoutnewacct işaret ettiği gibi, adres ve çekici. Ancak türün kendisi bir değer türüdür.

Sizin İçin Referans Türleri Ne Anlama Geliyor?

Başvuru türü nesne pratikte özeldir çünkü:

  • Yalnızca atama veya işleve geçiş, aynı nesneye birden fazla referans verebilir

  • Nesnenin kendisi, kendisine yapılan referans bir sabit ( letaçık veya zımni) olsa bile değiştirilebilir .

  • Nesneye yapılan bir mutasyon, nesneye yapılan tüm referanslarda görüldüğü şekliyle nesneyi etkiler.

Bunlar tehlike olabilir, bu yüzden gözünüzü dört açın. Öte yandan, bir referans türünü iletmek açıkça etkilidir çünkü yalnızca bir işaretçi kopyalanır ve iletilir ki bu önemsizdir.

Sizin İçin Değer Türleri Ne Anlama Geliyor?

Açıkça, bir değer türü geçirmek "daha güvenlidir" ve letsöylediği anlamına gelir: Bir yapı örneğini veya bir letreferans aracılığıyla bir numaralandırma örneğini değiştiremezsiniz . Öte yandan, bu güvenlik değerin ayrı bir kopyasını çıkarmakla sağlanıyor, değil mi? Bu, bir değer türünü geçirmeyi potansiyel olarak pahalı hale getirmez mi?

Hem evet hem hayır. Düşündüğün kadar kötü değil. Nate Cook'un dediği gibi, bir değer türünü iletmek, mutlaka kopyalama anlamına gelmez, çünkü let(açık veya zımni) değişmezliği garanti eder, bu nedenle hiçbir şeyi kopyalamaya gerek yoktur. Ve hatta bir içine geçen varreferans şeyler anlamına gelmez olacaktır onlar sadece o, kopyalanabilir olabilir (bir mutasyon var, çünkü) gerekirse olun. Dokümanlar, külotlarınızı bir bükülme haline getirmemenizi özellikle tavsiye ediyor.


6
"Sınıf örnekleri referansla aktarılır. Fonksiyonlar referansla iletilir" Hayır. Parametrenin inouttüründen bağımsız olmadığı durumlarda, değer ile geçiş yapılır . Bir şeyin referansla geçiş olup olmadığı türlere ortogonaldir.
newacct

5
@newacct Tabii ki tam anlamıyla haklısınız! Kesin olarak, her şeyin değere göre geçtiği, ancak numaralandırma örneklerinin ve yapı örneklerinin değer türleri olduğu ve sınıf örneklerinin ve işlevlerin başvuru türleri olduğu söylenmelidir . Örneğin, developer.apple.com/swift/blog/?id=10 adresine bakın - Ayrıca developer.apple.com/library/ios/documentation/Swift/Conceptual/… bakın. Ancak, söylediğim şeyin genel kelimelerin anlamı.
matt

6
Doğru ve değer türleri / başvuru türleri, değere göre geçirme / başvuruya göre geçirme ile karıştırılmamalıdır, çünkü değer türleri değere göre veya başvuruya göre iletilebilir ve başvuru türleri de değere göre veya başvuru yoluyla iletilebilir.
newacct

1
@newacct çok faydalı tartışma; Özetimi yanıltmasın diye yeniden yazıyorum.
matt

@newacct Yorumunuzu hala biraz yanıltıcı olarak görüyorum. Bunu okuyan ortalama bir kişi Swift uzmanı olmadığı için burada. Yalnızca her şeyin değere göre aktarıldığını belirtmek kafa karıştırıcıdır ve örneğin, bir referans türünü referans olarak iletmenin mümkün olduğunu, böylece büyük olasılıkla geliştiricinin niyeti olmayacak bir işaretçiye işaret etmenin mümkün olduğunu netleştirmez. Matt'in orijinal cevabı yerinde.
Adrian Bartholomew

44

Bu bir zaman geçmesi değer-ile- parametre değilken inout.

Bu bir zaman geçmesi-referans ile parametresi ise inout. Bununla birlikte, bu, &bir inoutparametreye geçerken bağımsız değişken üzerinde operatörü açıkça kullanmanız gerektiği gerçeğinden dolayı biraz karmaşıktır , bu nedenle, değişkeni doğrudan ilettiğiniz geleneksel geçiş referans tanımına uymayabilir.


3
Nate Cook'un ile birlikte bu cevap, hatta bir "referans türü" gerçeği etrafında (C ++ gelen) benim için daha net oldu değil fonksiyon kapsamı dışında değiştirilebilir sürece açıkça (kullanarak belirtin inout)
Gobe

10
inoutaslında gönderime göre iletilmez , kopyalanarak çıkarılır. Yalnızca işlev çağrısından sonra değiştirilen değerin orijinal argümana atanacağını garanti eder. Giriş Parametreleri
Dalija Prasnikar

1
her şeyin değerle geçtiği doğru olsa da. Referans türleri özellikleri, aynı örneğe kopyalanan referanslar olarak işlev içinde değiştirilebilir.
MrAn3

43

Swift'deki her şey varsayılan olarak "kopya" ile aktarılır, bu nedenle bir değer türünü ilettiğinizde değerin bir kopyasını alırsınız ve bir referans türünü ilettiğinizde tüm ima ettiği şekilde referansın bir kopyasını alırsınız. (Yani, referansın kopyası hala orijinal referansla aynı örneğe işaret eder.)

Yukarıdaki "kopya" nın etrafında korkutucu alıntılar kullanıyorum çünkü Swift pek çok optimizasyon yapıyor; Mümkün olan her yerde, bir mutasyon veya mutasyon olasılığı olana kadar kopyalanmaz. Parametreler varsayılan olarak değişmez olduğundan, bu çoğu zaman gerçekte hiçbir kopyanın gerçekleşmediği anlamına gelir.


Benim için bu en iyi cevaptır, çünkü örneğin örnek özelliklerinin, parametre bir kopya olsa bile (değere göre geçiş), aynı referansa işaret ettiği için fonksiyon içinde değiştirilebileceği açıklığa kavuşturulmuştur.
MrAn3

En iyi cevap.
Adrian Bartholomew

9

İşte referansla geçmek için küçük bir kod örneği. Güçlü bir nedeniniz yoksa bunu yapmaktan kaçının.

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

Böyle ara

var val1: String = "";
var val2: Int = -1;
ComputeSomeValues(&val1, &val2);

1
Neden bunu yapmaktan kaçınmalısınız?
Beyinsiz

1
@Beyinsiz çünkü koda gereksiz karmaşıklık katıyor. Parametreleri alıp tek bir sonuç döndürmek en iyisidir. Bunu yapmak zorunda kalmak, genellikle kötü tasarıma işaret eder. Bunu ifade etmenin başka bir yolu, iletilen referans değişkenlerdeki gizli yan etkilerin arayan için şeffaf olmamasıdır.
Chris Amelinckx

Bu referansla geçmez. inoutbir kopyalama, kopyalama operatörüdür. Önce nesneyi kopyalayacak, ardından işlev döndükten sonra orijinal nesnenin üzerine yazacaktır. Aynı görünse de, ince farklılıklar vardır.
Hannes Hertach

7

Elma Swift Geliştirici blog denilen bir yazı vardır Değer ve Referans Tipleri çok bu konuda net ve detaylı bir tartışma sağlar.

Alıntılamak:

Swift'deki türler iki kategoriden birine ayrılır: birincisi, "değer türleri"; burada her bir örneğin, genellikle yapı, enum veya tuple olarak tanımlanan verilerinin benzersiz bir kopyasını tutar. İkincisi, örneklerin verilerin tek bir kopyasını paylaştığı ve türün genellikle bir sınıf olarak tanımlandığı "başvuru türleri".

Swift blog yazısı, farklılıkları örneklerle açıklamaya devam ediyor ve birini diğerinin üzerine ne zaman kullanacağınızı öneriyor.


1
Bu soruya cevap vermiyor. Soru, referans türlerine karşı değer türlerine tamamen ortogonal olan değere göre geçirme ve başvuruya göre geçiş hakkındadır.
Jörg W Mittag

2

Sınıflar referanslarla aktarılır ve diğerleri varsayılan olarak değere göre aktarılır. inoutAnahtar kelimeyi kullanarak referans olarak geçebilirsiniz .


Bu yanlış. inoutbir kopyalama, kopyalama operatörüdür. Önce nesneyi kopyalayacak, ardından işlev döndükten sonra orijinal nesnenin üzerine yazacaktır. Aynı görünse de, ince farklılıklar vardır.
Hannes Hertach

2

+ = Gibi bir infix operatörü ile inout kullandığınızda, & adres sembolü göz ardı edilebilir. Sanırım derleyici referansla geçtiğini varsayıyor?

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

origDictionary + = newDictionaryToAdd

Ve güzel bir şekilde bu sözlük 'ekle' sadece orijinal referansa yazıyor, kilitlemek için harika!


2

Sınıflar ve yapılar

Yapılar ve sınıflar arasındaki en önemli farklardan biri, yapıların kodunuzda aktarıldıklarında her zaman kopyalanması, ancak sınıfların başvuruya göre aktarılmasıdır.

Kapanışlar

Bir sınıf örneğinin bir özelliğine bir kapanış atarsanız ve kapanış, örneğe veya üyelerine başvurarak bu örneği yakalarsa, closure ve örnek arasında güçlü bir referans döngüsü oluşturursunuz. Swift, bu güçlü referans döngülerini kırmak için yakalama listelerini kullanıyor

ARC (Otomatik Referans Sayma)

Referans sayma yalnızca sınıfların örnekleri için geçerlidir. Yapılar ve numaralandırmalar değer türleridir, başvuru türleri değildir ve başvuru ile depolanmaz ve aktarılmaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.