Bu cevap topluluk wiki'sidir . Daha iyi yapılabileceğini düşünüyorsanız, düzenlemekten çekinmeyin !
Arka Plan: İsteğe Bağlı Nedir?
Swift yılında Optional
bir olan genel tür (her türde) bir değeri içerebilir, ya da hiç bir değer.
Diğer birçok programlama dilinde, belirli bir "sentinel" değeri genellikle değer eksikliğini belirtmek için kullanılır . Örneğin Objective-C'de nil
( boş gösterici ) bir nesnenin eksikliğini gösterir. Ancak bu, ilkel türlerle çalışırken daha zorlaşır - -1
bir tamsayı, belki de INT_MIN
veya başka bir tam sayının yokluğunu göstermek için kullanılmalıdır ? Belirli bir değer "tamsayı yok" olarak seçilirse, bu artık geçerli olarak değerlendirilemez anlamına gelir değerin değer .
Swift, tür güvenli bir dildir, yani dil, kodunuzun çalışabileceği değer türleri hakkında net olmanıza yardımcı olur. Kodunuzun bir kısmı bir Dize bekliyorsa, security yazdığınızda kodu yanlışlıkla Int olarak iletmeniz engellenir.
Swift'te her tür isteğe bağlı yapılabilir . İsteğe bağlı bir değer, orijinal türden veya özel değerden herhangi bir değeri alabilir nil
.
Opsiyoneller ?
, tür üzerine bir sonekle tanımlanır :
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
İsteğe bağlı bir değerin olmaması aşağıdakilerle gösterilir nil
:
anOptionalInt = nil
( Bunun Objective-C ile nil
aynı olmadığını unutmayın nil
. Objective-C'de nil
geçerli bir nesne işaretçisinin olmamasıdır ; Swift'te Optionals nesnelerle / referans türleriyle sınırlı değildir. İsteğe bağlı Haskell'ın Belki gibi davranır .)
Neden “ önemli hata: İsteğe bağlı bir değer açılırken beklenmedik bir şekilde sıfır bulundu ” iletisi aldım ?
(Hepsi de varsa) isteğe bağlı bir değerini erişmek için aşağıdakileri yapmanız gerekir kakışıyor bunu. İsteğe bağlı bir değer güvenli veya zorla açılabilir. İsteğe bağlı unwrap-zorlamak ve bu takdirde vermedi bir değere sahip, programınız yukarıdaki mesajla kilitlenmesine.
Xcode, bir kod satırını vurgulayarak çökmeyi gösterir. Sorun bu satırda oluşur.
Bu çökme, iki farklı tür güç açma ile ortaya çıkabilir:
1. Açık Kuvvet Açma
Bu, !
isteğe bağlı olarak operatör ile yapılır . Örneğin:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Önemli hata: İsteğe bağlı bir değer açılırken beklenmedik bir şekilde nil bulundu
Burada anOptionalString
olduğu gibi , nil
açmaya zorladığınız hatta bir çarpışma yaşarsınız.
2. Örtük Olarak Açılmamış Seçenekler
Bunlar türden sonra !
değil, a ile tanımlanır ?
.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
Bu seçeneklerin bir değer içerdiği varsayılır. Bu nedenle, örtülü olarak açılmamış bir isteğe bağlı eriştiğinizde, sizin için otomatik olarak açılmaya zorlanacaktır. Bir değer içermiyorsa kilitlenir.
print(optionalDouble) // <- CRASH
Önemli hata: Opsiyonel bir değeri dolaylı olarak açarken beklenmedik bir şekilde nil bulundu
Hangi değişkenin çökmeye neden ⌥olduğunu bulmak için, tanımı göstermek üzere tıklatarak isteğe bağlı türü nerede bulabileceğinizi basılı tutabilirsiniz .
Özellikle IBOutlet'ler genellikle örtük olarak paketlenmemiş seçeneklerdir. Bunun nedeni, xib'inizin veya film şeridinizin başlatma işleminden sonra çalışma zamanında çıkışları birbirine bağlamasıdır . Bu nedenle çıkışlara yüklenmeden önce erişmediğinizden emin olmalısınız. Bağlantıların film şeridi / xib dosyanızdaki doğru olup olmadığını da kontrol etmelisiniz, aksi takdirde değerler nil
çalışma zamanında olacak ve dolayısıyla örtük olarak açılmadıklarında çökecektir. . Bağlantıları düzeltirken, çıkışlarınızı tanımlayan kod satırlarını silmeyi deneyin, ardından tekrar bağlayın.
Ne zaman bir İsteğe bağlı paketini açmaya zorlamalıyım?
Açık kuvvet unwrapping
Genel bir kural olarak, hiçbir zaman !
operatöre bağlı olarak isteğe bağlı bir paketin açılmasını zorlamamanız gerekir . Kullanıldığı durumlar olabilir!
kabul edilebilir - ancak yalnızca isteğe bağlı bir değer içerdiğinden% 100 eminseniz kullanmalısınız.
Oradayken edebilir bir bildiğimiz gibi, kuvvet unwrapping kullanabileceğiniz bir fırsat olabilir aslında isteğe bağlı bir değer içerdiğini - bir yok tek yer nerede olamaz güvenle unwrap o isteğe yerine.
Örtük Olarak Açılmamış Seçenekler
Bu değişkenler, atamalarını daha sonra kodunuzda erteleyebileceğiniz şekilde tasarlanmıştır. Öyle senin buraya erişmek önce onlar bir değere sahip olmak için sorumluluk. Bununla birlikte, kuvvetin açılmasını içerirler, çünkü doğal olarak güvensizdirler - varsaydıkları gibi nil atamak geçerli olsa bile, değerinizin sıfır için .
Son çare olarak yalnızca örtük olarak açılmamış opsiyonelleri kullanmalısınız . Tembel bir değişken kullanabilir veya bir değişken için varsayılan bir değer sağlayabilirseniz , bunu örtük olarak kaydırılmamış bir isteğe bağlı kullanmak yerine yapmalısınız.
Bununla birlikte, örtük olarak açılmamış seçeneklerin yararlı olduğu birkaç senaryo vardır ve bunları aşağıda listelenen şekilde güvenli bir şekilde açmanın çeşitli yollarını kullanabilirsiniz - ancak bunları her zaman dikkatli bir şekilde kullanmalısınız.
Seçeneklerle nasıl güvenli bir şekilde başa çıkabilirim?
İsteğe bağlı bir değer içerip içermediğini kontrol etmenin en basit yolu, değeri karşılaştırmaktır nil
.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
Bununla birlikte, opsiyonellerle çalışırken zamanın% 99,9'u, eğer bir tane içeriyorsa, içerdiği değere erişmek istersiniz. Bunu yapmak için, İsteğe Bağlı Bağlama'yı kullanabilirsiniz .
İsteğe Bağlı Ciltleme
İsteğe Bağlı Bağlama, isteğe bağlı bir değer içerip içermediğini kontrol etmenizi sağlar ve kaydırılmamış değeri yeni bir değişkene veya sabite atamanıza olanak tanır. Sözdizimini kullanır if let x = anOptional {...}
veyaif var x = anOptional {...}
bağladıktan sonra yeni değişkenin değerini değiştirmeniz gerekiyorsa.
Örneğin:
if let number = anOptionalInt {
print("Contains a value! It is \(number)!")
} else {
print("Doesn’t contain a number")
}
Bunun yaptığı ilk önce isteğe bağlı bir değer olup olmadığını kontrol etmektir. O takdirde yapar , daha sonra 'Çizelgesi' değerinin yeni değişken (atanır number
dışı seçmeli sanki o zaman serbestçe kullanabileceği -). İsteğe bağlı değilse gelmez bir değer içeren beklediğiniz gibi, o zaman başka bir fıkra, çağrılır.
İsteğe bağlı ciltlemeyle ilgili düzgün olan şey, aynı anda birden fazla seçeneği açabilmenizdir. İfadeleri virgülle ayırabilirsiniz. Tüm seçeneklerin kaydırılmamış olması durumunda ifade başarılı olacaktır.
var anOptionalInt : Int?
var anOptionalString : String?
if let number = anOptionalInt, let text = anOptionalString {
print("anOptionalInt contains a value: \(number). And so does anOptionalString, it’s: \(text)")
} else {
print("One or more of the optionals don’t contain a value")
}
Başka bir düzgün hile, açtıktan sonra değer üzerinde belirli bir koşulu kontrol etmek için virgül kullanabilmenizdir.
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}
Bir if ifadesinde isteğe bağlı bağlama kullanmanın tek yakalaması, yalnızca ifadenin kapsamından açılmamış değere erişebilmenizdir. İfadenin kapsamı dışından değere erişmeniz gerekiyorsa, bir koruma ifadesi kullanabilirsiniz .
Bir koruma ifadesi , başarı için bir koşul tanımlamanıza olanak tanır ve geçerli kapsam yalnızca bu koşul yerine getirildiğinde yürütülmeye devam eder. Sözdizimi ile tanımlanırlar guard condition else {...}
.
Bu nedenle, isteğe bağlı bir bağlama ile kullanmak için şunları yapabilirsiniz:
guard let number = anOptionalInt else {
return
}
(Koruma gövdesi içinde, şu anda yürütülen kodun kapsamından çıkmak için kontrol aktarma deyimlerinden birini kullanmanız gerektiğini unutmayın ).
Eğer anOptionalInt
bir değer içerir, bu Çizelgesi ve yeni atanmış olacak number
sabiti. Muhafızdan sonraki kod çalışmaya devam edecektir. Bir değer içermiyorsa - koruma kodu parantez içinde yürütür, bu da kontrolün aktarılmasına yol açar, böylece hemen sonraki kod yürütülmez.
Guard ifadeleriyle ilgili gerçek temiz şey, kaydırılmamış değerin artık ifadeyi izleyen kodda kullanılabilmesidir (gelecekteki kodun yalnızca isteğe bağlı bir değere sahipse çalışabileceğini bildiğimiz için ). Bu, birden fazla if ifadesini iç içe yerleştirerek oluşturulan 'kıyamet piramitlerini' ortadan kaldırmak için mükemmeldir .
Örneğin:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
Muhafızlar ayrıca, if ifadesinin desteklediği aynı düzgün hileleri destekler; örneğin, aynı anda birden fazla seçeneğin paketinin açılması ve where
yan tümce kullanılması.
Bir if veya guard ifadesi kullanıp kullanmadığınız, gelecekteki herhangi bir kodun isteğe bağlı bir değer içermesini gerektirip gerektirmediğine bağlıdır .
Nil Birleştirme Operatörü
Nil Birleştirme Operatörü bir şık stenografi sürümü üçlü koşullu operatör öncelikle dışı seçeneklere için isteğe bağlı öğeleri dönüştürmek için tasarlanmıştır. Sözdizimine sahiptir a ?? b
, burada a
isteğe bağlı bir türdür ve b
aynı türdür a
(genellikle isteğe bağlı olmasa da).
Temelde “ a
Bir değer içeriyorsa, paketini açın. b
Bunun yerine geri dönmezse ”. Örneğin, şu şekilde kullanabilirsiniz:
let number = anOptionalInt ?? 0
Bu , bir değer içeriyorsa değerini içeren başka bir tür number
sabitini tanımlayacaktır .Int
anOptionalInt
0
Sadece kısayol için:
let number = anOptionalInt != nil ? anOptionalInt! : 0
İsteğe Bağlı Zincirleme
Bir yöntemi çağırmak veya isteğe bağlı bir özelliğe erişmek için İsteğe Bağlı Zincirleme özelliğini kullanabilirsiniz . Bu, değişken adını ?
kullanırken bir ile ekleyerek yapılır .
Örneğin, foo
isteğe bağlı bir Foo
örnek türünde bir değişkenimiz olduğunu varsayalım.
var foo : Foo?
Bir foo
şey döndürmeyen bir yöntem çağırmak istersek, şunları yapabiliriz:
foo?.doSomethingInteresting()
Eğer foo
bir değer içerir, bu yöntem üzerinde çağrılır. Olmazsa, kötü bir şey olmaz - kod yürütmeye devam eder.
(Bu, nil
Objective-C'de ileti göndermeye benzer davranıştır )
Bu nedenle bu, özellikleri ve çağrı yöntemlerini ayarlamak için de kullanılabilir. Örneğin:
foo?.bar = Bar()
Yine, hiçbir şey kötü burada da olacak foo
olan nil
. Kodunuz çalışmaya devam edecektir.
İsteğe bağlı zincirlemenin yapmasına izin veren bir başka düzgün hile, bir özellik ayarlamanın veya bir yöntemi çağırmanın başarılı olup olmadığını kontrol etmektir. Bunu, dönüş değerini ile karşılaştırarak yapabilirsiniz nil
.
(Bunun nedeni, hiçbir şey döndürmeyen bir yöntem Void?
yerine isteğe bağlı bir değerin Void
döndürülmesidir)
Örneğin:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
Ancak, değer döndüren özelliklere veya çağrı yöntemlerine erişmeye çalışırken işler biraz daha zorlaşır. Çünkü foo
isteğe bağlıdır, ondan dönen şey opsiyonel olacak. Bununla başa çıkmak için, yukarıdaki yöntemlerden birini kullanarak döndürülen seçeneklerin paketini açabilir veya foo
yöntemlere erişmeden veya değerleri döndüren yöntemleri çağırmadan önce kendisini açabilirsiniz .
Ayrıca, adından da anlaşılacağı gibi, bu ifadeleri birbirine 'zincirleyebilirsiniz'. Bu foo
, bir özelliği baz
olan isteğe bağlı bir özelliğe sahipse qux
, aşağıdakileri yazabileceğiniz anlamına gelir :
let optionalQux = foo?.baz?.qux
Yine foo
ve baz
isteğe bağlı olduğundan, kendisinden isteğe bağlı qux
olup olmadığına bakılmaksızın, döndürülen değer her zaman isteğe qux
bağlı olacaktır.
map
ve flatMap
Optionals ile sıklıkla az kullanılan bir özellik map
ve flatMap
işlevlerini kullanma yeteneğidir . Bunlar isteğe bağlı olmayan dönüşümleri isteğe bağlı değişkenlere uygulamanızı sağlar. İsteğe bağlı bir değere sahipse, belirli bir dönüşümü uygulayabilirsiniz. Eğer bir değeri yoksa kalır nil
.
Örneğin, isteğe bağlı bir dizeniz olduğunu varsayalım:
let anOptionalString:String?
map
Fonksiyonu ona uygulayarak - stringByAppendingString
fonksiyonu başka bir dizeye birleştirmek için kullanabiliriz.
Çünkü stringByAppendingString
isteğe bağlı olmayan bir string argüman alıyor, doğrudan giriş opsiyonlu dize olamaz. Bununla birlikte, kullanarak map
, bir değeri stringByAppendingString
varsa izin kullanılmasını kullanabiliriz anOptionalString
.
Örneğin:
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
Ancak, anOptionalString
bir değeri map
yoksa dönecektir nil
. Örneğin:
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap
Kapatma gövdesi içinden başka bir isteğe bağlı map
geri dönmenize izin vermesi dışında, buna benzer şekilde çalışır . Bu, isteğe bağlı olmayan bir girdi gerektiren, ancak isteğe bağlı bir çıktı alabileceğiniz bir işleme isteğe bağlı girebileceğiniz anlamına gelir.
try!
Swift'in hata işleme sistemi Do-Try-Catch ile güvenle kullanılabilir :
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
Bir someThrowingFunc()
hata atarsa, hata güvenli bir şekildecatch
blokta .
error
Eğer gördüğünüz sabit catch
bloğu tarafımızca beyan edilmiş - otomatik araya getirilen catch
.
Ayrıca error
kendinizi beyan edebilirsiniz , yararlı bir formata dökme avantajına sahiptir, örneğin:
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
try
Bu şekilde kullanmak , fırlatma işlevlerinden kaynaklanan hataları denemenin, yakalamanın ve işlemenin doğru yoludur.
Orada da try?
hata emdiği:
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there's no error information available
}
Ancak Swift'in hata işleme sistemi aşağıdakileri de "zorlamaya" zorlamanın bir yolunu sunar try!
:
let result = try! someThrowingFunc()
Bu yayında açıklanan kavramlar burada da geçerlidir: bir hata atılırsa uygulama çökecektir.
Sadece try!
sonucunun asla bağlamınızda başarısız olmayacağını kanıtlayabiliyorsanız kullanmalısınız - ve bu çok nadirdir.
Çoğu zaman try?
, hatayı işlemenin önemli olmadığı nadir durumlarda , tam Do-Try-Catch sistemini ve isteğe bağlı olanı kullanırsınız .
kaynaklar