Swift'te “sarılmamış değer” nedir?


155

Bu öğreticiyi takip ederek iOS 8 / OSX 10.10 için Swift öğreniyorum ve " kaydırılmamış değer " terimi, bu paragrafta olduğu gibi ( Nesneler ve Sınıf altında ) birkaç kez kullanılır :

İsteğe bağlı değerlerle çalışırken yazabilirsiniz? yöntemler, özellikler ve abonelik gibi işlemlerden önce. Önce değeri? Nil, sonra her şey? yok sayılır ve tüm ifadenin değeri sıfırdır. Aksi takdirde, isteğe bağlı değer paketinden çıkarılır ve? kaydırılmamış değere göre hareket eder . Her iki durumda da, tüm ifadenin değeri isteğe bağlı bir değerdir.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare?.sideLength

Anlamadım ve şans olmadan web'de arama yaptım.

Bu ne anlama geliyor?


Düzenle

Cezary'nin cevabından, orijinal kodun çıktısı ile nihai çözüm (oyun alanında test edildi) arasında küçük bir fark var:

Orijinal kod

Orijinal kod

Cezary'nin çözümü

Cezary'nin çözümü

Üst sınıfın özellikleri ikinci durumda çıktıda gösterilirken, birinci durumda boş bir nesne vardır.

Her iki durumda da sonucun aynı olması gerekmez mi?

İlgili Sorular ve Yanıtlar: Swift'te isteğe bağlı değer nedir?


1
Cezary'nin cevabı yerinde. Ayrıca, iBooks'ta ÜCRETSİZ
Daniel Storm

@DanielStormApps Bu iBook benim sorum bağlantılı öğretici taşınabilir bir sürümüdür :)
Bigood

Yanıtlar:


289

İlk olarak, İsteğe bağlı bir türün ne olduğunu anlamalısınız. İsteğe bağlı bir tür temel olarak değişkenin olabileceği anlamına gelir nil.

Misal:

var canBeNil : Int? = 4
canBeNil = nil

Soru işareti canBeNilolabileceği gerçeğini gösterir nil.

Bu işe yaramaz:

var cantBeNil : Int = 4
cantBeNil = nil // can't do this

Değişkeninizden isteğe bağlı değeri almak için, değerinin paketini açmanız gerekir . Bu, sonuna bir ünlem işareti koymak anlamına gelir.

var canBeNil : Int? = 4
println(canBeNil!)

Kodunuz şöyle görünmelidir:

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare!.sideLength

Bir sidenote:

Soru işareti yerine bir ünlem işareti kullanarak seçeneklerin otomatik olarak açılacağını da bildirebilirsiniz.

Misal:

var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed

Kodunuzu düzeltmenin alternatif bir yolu:

let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare.sideLength

DÜZENLE:

Gördüğünüz fark tam olarak isteğe bağlı değerin sarılmış olduğunun belirtisidir . Üstünde başka bir katman daha var. Çizelgesi o, iyi, açılmamış olduğu için sürüm sadece düz nesneyi gösterir.

Hızlı oyun alanı karşılaştırması:

oyun alanı

Birinci ve ikinci durumlarda, nesne otomatik olarak açılmaz, bu nedenle iki "katman" ( {{...}}) görürsünüz, üçüncü durumda ise {...}, nesne otomatik olarak açıldığı için yalnızca bir katman ( ) görürsünüz .

İlk durum ve ikinci iki durum arasındaki fark, ikinci iki vakanın, eğer optionalSquareayarlanırsa size bir çalışma zamanı hatası vermesidir nil. İlk durumda sözdizimini kullanarak, böyle bir şey yapabilirsiniz:

if let sideLength = optionalSquare?.sideLength {
    println("sideLength is not nil")
} else {
    println("sidelength is nil")
}

1
Açık açıklama için teşekkürler! Soruma dahil edilen kod Apple'ın öğreticisinden alıntılanıyor (sorudaki bağlantı) ve sanırım olduğu gibi çalışması gerekiyor (ve öyle). Yine de, nihai çözümünüz ve orijinal çözümünüz farklı sonuçlar verir (düzenlememe bakın). Herhangi bir düşünce?
Bigood

2
Güzel. Harika bir açıklama. Yine de kafam karıştı. Neden isteğe bağlı bir değer kullanmak isteyeyim? Ne zaman yararlıdır? Apple neden bu davranışı ve sözdizimini yarattı?
Todd Lehman

1
Özellikle sınıf özellikleri söz konusu olduğunda önemlidir. Swift init(), sınıfın işlevinde isteğe bağlı olmayan tüm yöntemlerin başlatılmasını gerektirir . Apple tarafından yayınlanan Swift kitabında "Temel Bilgiler" (isteğe bağlı kapakları), "Başlatma" ve "İsteğe Bağlı Zincirleme" bölümlerini okumanızı tavsiye ederim.
Cezary Wojcik

2
@ToddLehman Apple, çok daha güvenli bir kod yazmanıza yardımcı olmak için bunu yarattı. Birisi null olup olmadığını kontrol etmeyi unuttuğu için bir program çökmesini Java tüm boş işaretçi istisnalar düşünün. Veya Objective-C'deki garip davranışlar çünkü nil'i kontrol etmeyi unuttun. İsteğe bağlı tip ile nil ile uğraşmayı unutamazsınız. Derleyici sizi bununla başa çıkmaya zorlar.
Erik Engheim

5
@AdamSmith - Bir Java arka planından geliyor, kesinlikle seçeneklerin kullanımını görebiliyorum ... ama aynı zamanda (daha yakın zamanda) Objective-C arka planından geliyor, hala kullanımını görmede sorun yaşıyorum çünkü Objective- C açıkça nil nesnelere mesaj göndermenizi sağlar. Hmm. Birinin kodu kullanmayan eşdeğer koddan daha kısa ve daha güvenli hale getirmeye nasıl yardımcı olduklarını gösteren bir örnek yazdığını görmek isterim. Yararlı olmadıklarını söylemiyorum; Sadece "anlamadım" diyorum.
Todd Lehman

17

Mevcut doğru cevap harika, ama bunu tam olarak anlayabilmem için iyi bir benzetmeye ihtiyacım olduğunu buldum, çünkü bu çok soyut ve garip bir kavram.

Öyleyse, doğru cevaba ek olarak farklı bir perspektif vererek "sağ beyinli" (görsel düşünme) geliştiricilere yardım edeyim. İşte bana çok yardımcı olan iyi bir benzetme.

Doğum Günü Hediyesi Sarma Analojisi

Opsiyonelleri sert, sert, renkli sargılı doğum günü hediyeleri gibi düşünün.

Şimdiki ambalajı açana kadar ambalajın içinde bir şey olup olmadığını bilmiyorsunuz - belki içinde hiçbir şey yoktur! İçinde bir şey varsa, o da sarılı olan ve hiçbir şey içermeyen başka bir hediye olabilir . Sonunda sarmadan başka bir şey olmadığını keşfetmek için 100 iç içe hediyeyi bile açabilirsiniz .

İsteğe bağlı değer değilse nil, şimdi bir şey içeren bir kutu gösterdiniz . Ancak, özellikle değer açıkça yazılmadıysa ve bir değişken ve önceden tanımlanmış bir sabit değilse , kutuda ne olduğu, ne tür olduğu veya ne olduğu gibi belirli bir şey bilmeden önce kutuyu açmanız gerekebilir . gerçek değer .

Kutuda ne var?! analoji

Değişkeni açtıktan sonra bile , SE7EN'in son sahnesinde ( uyarı : spoiler ve çok R dereceli faul dili ve şiddet) Brad Pitt gibisiniz , çünkü şimdiki zamanı açtıktan sonra bile, aşağıdaki durumdasınız: şimdi nilveya bir şey içeren bir kutunuz var (ama ne olduğunu bilmiyorsunuz).

Bir şeyin türünü biliyor olabilirsiniz . Örneğin, değişkeni tür olarak bildirdiyseniz, [Int:Any?]herhangi bir eski türün sarılmış içeriğini veren tamsayı aboneliklerine sahip (potansiyel olarak boş) bir sözlüğünüz olduğunu bilirsiniz.

Bu yüzden Swift'teki koleksiyon türleriyle (Sözlükler ve Diziler) uğraşmak biraz kıllı olabilir.

Konuşma konusu olan mesele:

typealias presentType = [Int:Any?]

func wrap(i:Int, gift:Any?) -> presentType? {
    if(i != 0) {
        let box : presentType = [i:wrap(i-1,gift:gift)]
        return box
    }
    else {
        let box = [i:gift]
        return box
    }
}

func getGift() -> String? {
    return "foobar"
}

let f00 = wrap(10,gift:getGift())

//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.

var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])

//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is

let asdf : String = b4r!! as! String

print(asdf)

nice 😊 😊😊 😊 😊😊 😊 😊😊
SamSol

benzetmeyi seviyorum! o zaman kutuları açmanın daha iyi bir yolu var mı? kod örneğinizde
David T.

Schrödinger'in kedisi kutuda
Jacksonkr

Kafamı karıştırdığın için teşekkürler .... Ne tür bir programcı boş bir Doğum Günü Hediyesi veriyor? tür demek
delta2flat

@Jacksonkr Ya da değil.
amsmath

13

Swift, tip güvenliğe yüksek bir prim verir. Swift dilinin tamamı güvenlik göz önünde bulundurularak tasarlanmıştır. Swift'in ayırt edici özelliklerinden biridir ve açık kollarla hoş karşılanmalısınız. Temiz, okunabilir kodun geliştirilmesine yardımcı olur ve uygulamanızın çökmesini engeller.

Swift'teki tüm seçenekler ?sembolle sınırlandırılmıştır . Ayarlayarak ?aslında olan isteğe bağlı olarak ilan edildiği türün adını sonra döküm türü olarak bu değil ki öncedir ?ama bunun yerine, isteğe bağlı tip.


Not: Bir değişken veya tür Intile aynı değildirInt? . Birbirlerine çalıştırılamayan iki farklı tiptir.


İsteğe Bağlı Kullanma

var myString: String?

myString = "foobar"

Bu mu değil sen bir tür ile çalışan anlamına String. Bu, bir tür String?(İsteğe Bağlı Dize veya İsteğe Bağlı Dize) ile çalıştığınız anlamına gelir . Aslında, ne zaman

print(myString)

çalışma zamanında, hata ayıklama konsolu yazdırılır Optional("foobar"). " Optional()" Kısmı, bu değişkenin çalışma zamanında bir değere sahip olabileceğini veya olmayabileceğini gösterir, ancak şu anda "foobar" dizesini içeriyor. Bu " Optional()" gösterge, isteğe bağlı değer "paketini açma" olarak adlandırılan şeyi yapmadığınız sürece kalacaktır.

İsteğe bağlı paketin kaldırılması , artık bu türü isteğe bağlı olmayan olarak yayınladığınız anlamına gelir. Bu yeni bir tür oluşturur ve bu isteğe bağlı olarak bulunan değeri yeni isteğe bağlı olmayan türe atar. Bu şekilde, derleyici tarafından katı bir değere sahip olması garanti edildiği için, bu değişken üzerinde işlem yapabilirsiniz.


Koşullu olarak Açma , isteğe bağlı değerin olup olmadığını kontrol eder nil. Değilse nil, değer ve atanacak yeni oluşturulan sabit değişken olacaktır Çizelgesi isteğe bağlı olmayan sabiti içine. Ve oradan ifblokta isteğe bağlı olmayanları güvenle kullanabilirsiniz .

Not: Koşullu olarak açılmamış sabitinize, açtığınız isteğe bağlı değişkenle aynı adı verebilirsiniz.

if let myString = myString {
    print(myString)
    // will print "foobar"
}

Koşullu olarak açılma seçenekleri, isteğe bağlı bir değere erişmenin en temiz yoludur, çünkü bir nil değeri içeriyorsa, if let bloğundaki her şey yürütülmez. Elbette, herhangi bir if ifadesi gibi, başka bir blok da ekleyebilirsiniz

if let myString = myString {
    print(myString)
    // will print "foobar"
}
else {
    print("No value")
}

Zorla Açma! ("bang") operatörü olarak bilinen şey kullanılarak yapılır . Bu daha az güvenlidir ancak yine de kodunuzun derlenmesine izin verir. Ancak patlama operatörünü her kullandığınızda, değişkeninizin zorla açmadan önce gerçekte katı bir değer içerdiğinden% 1000 emin olmalısınız.

var myString: String?

myString = "foobar"

print(myString!)

Bu tamamen geçerli Swift kodudur. Bunun myString"foobar" olarak ayarlanmış değerini yazdırır . Kullanıcı foobarkonsolda yazdırılmış görürdü ve hepsi bu kadar. Ancak değerin asla ayarlanmadığını varsayalım:

var myString: String?

print(myString!) 

Şimdi elimizde farklı bir durum var. Objective-C'den farklı olarak, isteğe bağlı bir şekilde zorla açma girişiminde bulunulduğunda ve isteğe bağlı ayarlanmadı ve niluygulamanızın içinde ne olduğunu görmek için isteğe bağlı olanı açmaya çalıştığınızda.


Tip Döküm ile Ambalajdan Çıkarma . Daha önce de belirttiğimiz gibi unwrapping, isteğe bağlı olduğunuzda, aslında isteğe bağlı olmayan bir türe döküm yapıyorsunuz, isteğe bağlı olmayanı farklı bir türe de dökebilirsiniz. Örneğin:

var something: Any?

Kodumuzda bir yerlerde değişken somethingbir değerle ayarlanır. Belki jenerikler kullanıyoruz ya da belki de bunun değişmesine neden olacak başka bir mantık var. Kodumuzda daha sonra kullanmak istiyoruz, somethingancak farklı bir türse yine de farklı şekilde tedavi edebiliyoruz. Bu durumda, bunu asbelirlemek için anahtar kelimeyi kullanmak istersiniz :

Not: asOperatör, Swift'te cast türünü yazmanızdır.

// Conditionally
if let thing = something as? Int {
    print(thing) // 0
}

// Optionally
let thing = something as? Int
print(thing) // Optional(0)

// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised

İki asanahtar kelime arasındaki farka dikkat edin . Daha önce olduğu gibi, isteğe bağlı olarak paketini açtığımızda !patlama operatörünü kullandık . Burada aynı şeyi yapacaksınız, ancak sadece isteğe bağlı olmayan bir döküm yapmak yerine, aynı zamanda döküm yapıyorsunuz Int. Ve aksi takdirde, değer uygulamanız çöktüğünde patlama operatörünü kullanmak gibi mahzun olmalıdır .Intnil

Ve bu değişkenleri bir çeşit veya matematiksel işlemde kullanmak için, bunun yapılabilmesi için paketlerinin açılması gerekir.

Örneğin, Swift'te yalnızca aynı türden geçerli sayıdaki veri türleri birbiriyle çalıştırılabilir. Eğer bir tür dökme zaman as!sen sanki o değişkenin mahzun zorluyor belli o ameliyat ve uygulamanın çökmesine neden nedenle güvenli, o tiptedir. Değişken gerçekten döküm yaptığınız tipte olduğu sürece bu tamam, aksi takdirde ellerinizde bir karışıklık olacak.

Bununla birlikte döküm as!, kodunuzun derlenmesine izin verecektir. Bir ile döküm yapmak as?farklı bir hikaye. Aslında, hepinizi tamamen farklı bir veri türü olarak as?ilan eder Int.

Şimdi Optional(0)

Ve eğer ödevinizi yapmaya çalışırsanız,

1 + Optional(1) = 2

Matematik öğretmeniniz size muhtemelen bir "F" verecektir. Swift ile aynı. Swift dışında size not vermek yerine hiç derleme yapmaz. Çünkü günün sonunda isteğe bağlı aslında sıfır olabilir .

Önce Güvenlik Çocuklar.


İsteğe bağlı türlerin güzel açıklaması. Benim için işleri temizlemeye yardımcı oldu.
Tobe_Sta

0

'?'

"!", isteğe bağlı zincirleme ifade anlamına gelir.
resim açıklamasını buraya girin

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.