"Önemli hata: İsteğe bağlı bir değeri açarken beklenmedik şekilde sıfır bulundu" ne anlama geliyor?
Swift programım çöküyor EXC_BAD_INSTRUCTION
ve aşağıdaki benzer hatalardan biri. Bu hata ne anlama geliyor ve bunu nasıl düzeltirim?
Önemli hata: İsteğe bağlı bir değeri açarken beklenmedik şekilde sıfır bulundu
veya
Önemli hata: İsteğe bağlı bir değeri örtük olarak açarken beklenmedik şekilde sıfır bulundu
Bu gönderi, "beklenmedik bir şekilde sıfır bulundu" sorunlarının yanıtlarını toplamayı amaçlamaktadır, böylece dağınık değil ve bulunması zor. Kendi cevabınızı eklemekten veya mevcut wiki cevabınızı düzenlemekten çekinmeyin .
Yanıtlar
Bu cevap topluluk wikisidir . Daha iyi hale getirilebileceğini düşünüyorsanız, düzenlemekten çekinmeyin !
Arka Plan: İsteğe Bağlı Nedir?
Swift'de Optional<Wrapped>
bir seçenek türüdür : orijinal ("Sarılmış") türden herhangi bir değer içerebilir veya hiç değer içermeyebilir (özel değer nil
). İsteğe bağlı değer olmalıdır Çizelgesi o kullanılmadan önce.
İsteğe bağlı bir olduğu genel tür araçlarının, Optional<Int>
ve Optional<String>
değişik tip - tipi içinde <>
ambalajlanmış türü olarak adlandırılır. Başlık altında, bir İsteğe bağlı iki durumu olan bir numaralandırmadır : .some(Wrapped)
ve .none
, burada .none
eşdeğerdir nil
.
İsteğe bağlı ifadeler, adlandırılmış tür kullanılarak Optional<T>
veya (en yaygın olarak) bir ?
son ekle birlikte bir kısaltma olarak bildirilebilir .
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int> // equivalent to `Int?`
anOptionalInt = nil // now this variable contains nil instead of an integer
İsteğe bağlı ifadeler, kod yazarken varsayımlarınızı ifade etmek için basit ama güçlü bir araçtır. Derleyici, hata yapmanızı önlemek için bu bilgileri kullanabilir. Gönderen Swift Programlama Dili :
Swift, tür açısından güvenli bir dildir; bu, dilin, kodunuzun birlikte çalışabileceği değer türleri hakkında net olmanıza yardımcı olduğu anlamına gelir. Kodunuzun bir parçası bir
String
, tür güvenliği gerektiriyorsa ,Int
yanlışlıkla geçmenizi engeller . Aynı şekilde, tür güvenliği, isteğe bağlıString
olanı yanlışlıkla isteğe bağlı olmayan bir kod parçasına geçirmenizi engellerString
. Tür güvenliği, geliştirme sürecinde hataları olabildiğince erken yakalamanıza ve düzeltmenize yardımcı olur.
Diğer bazı programlama dillerinin de genel seçenek türleri vardır : örneğin, Haskell'de Belki , Rust'ta seçenek ve C ++ 17'de isteğe bağlı .
Seçenek türleri olmayan programlama dillerinde , belirli bir "gözlemci" değer genellikle geçerli bir değerin olmadığını belirtmek için kullanılır. Örneğin Objective-C'de nil
( boş işaretçi ) bir nesnenin eksikliğini temsil eder. Gibi ilkel türler int
için boş gösterici kullanılamaz, bu nedenle ayrı bir değişkene ( value: Int
ve gibi isValid: Bool
) veya belirlenmiş bir gözlemci değere ( -1
veya gibi INT_MIN
) ihtiyacınız olacaktır. Bu yaklaşımlar hataya açıktır çünkü isValid
sentinel değerini kontrol etmeyi veya kontrol etmeyi unutmak kolaydır . Ayrıca, gözlemci olarak belirli bir değer seçilirse, bu artık geçerli bir değer olarak değerlendirilemeyeceği anlamına gelir .
Swift'inki gibi seçenek türleri Optional
, özel, ayrı bir nil
değer sunarak (böylece bir gözlemci değer atamak zorunda kalmazsınız) ve derleyicinin gerektiğinde nil'i kontrol etmeyi hatırlamanıza yardımcı olması için güçlü tür sisteminden yararlanarak bu sorunları çözer .
Neden " Önemli hata: İsteğe bağlı bir değeri açarken beklenmedik bir şekilde sıfır bulundu " mesajı 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 size çökmeyi gösterecektir. Sorun bu hatta ortaya çıkıyor.
Bu çökme, iki farklı türde zorla açmayla ortaya çıkabilir:
1. Açıkça Sarmalamayı Zorla Açma
Bu, !
isteğe bağlı olarak operatörle yapılır . Örneğin:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Önemli hata: İsteğe bağlı bir değeri açarken beklenmedik şekilde sıfır bulundu
Burada anOptionalString
olduğu gibi , nil
onu açmaya zorladığınız hatta bir çökme yaşayacaksınız.
2. Örtülü Olarak Sarılmamış Opsiyonlar
Bunlar , türden sonra !
a yerine a ile tanımlanır ?
.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
Bu opsiyonların bir değer içerdiği varsayılır. Bu nedenle, örtülü olarak açılmamış isteğe bağlı bir seçeneğe her eriştiğinizde, sizin için otomatik olarak zorla açılacak. Bir değer içermiyorsa çökecektir.
print(optionalDouble) // <- CRASH
Önemli hata: İsteğe bağlı bir değeri örtük olarak açılırken beklenmedik şekilde sıfır bulundu
Hangi değişkenin çökmeye neden ⌥olduğunu bulmak için, isteğe bağlı türü bulabileceğiniz tanımı göstermek için tıklatırken basılı tutabilirsiniz .
IBOutlet'ler, özellikle, genellikle örtülü olarak açılmamış opsiyonellerdir. Bunun nedeni, xib'inizin veya film şeridinizin çıkışları başlattıktan sonra çalışma zamanında birbirine bağlamasıdır . Bu nedenle, çıkışlara yüklenmeden önce erişmediğinizden emin olmalısınız. Ayrıca, film şeridi / xib dosyanızdaki bağlantıların doğru olup olmadığını da kontrol etmelisiniz, aksi takdirde değerler nil
çalışma zamanında olacak ve dolayısıyla örtülü olarak açılmadıklarında çökecektir. . Bağlantıları düzeltirken, çıkışlarınızı tanımlayan kod satırlarını silmeyi ve ardından bunları yeniden bağlamayı deneyin.
Bir İsteğe Bağlı Olarak Ne Zaman Zorla Açmalıyım?
Açık Zorla Açma
Genel bir kural olarak, !
operatörle isteğe bağlı bir seçeneği asla açık bir şekilde açmaya zorlamamalısınız . Kullanımın !
kabul edilebilir olduğu durumlar olabilir - ancak bunu yalnızca isteğe bağlı öğenin 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ülü Olarak Sarılmamış Opsiyonlar
Bu değişkenler, atamalarını kodunuzda daha sonraya erteleyebilmeniz için tasarlanmıştır. Öyle senin buraya erişmek önce onlar bir değere sahip olmak için sorumluluk. Bununla birlikte, zorla açmayı içerdikleri için, doğaları gereği hala güvensizdirler - sıfır atamak geçerli olsa bile değerinizin sıfır olmadığını varsayarlar .
Son çare olarak yalnızca örtük olarak sarmalanmamış seçenekleri kullanmalısınız . Tembel bir değişken kullanabilir veya bir değişken için varsayılan bir değer sağlayabiliyorsanız , bunu örtük olarak kapatı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 listelendiği gibi güvenli bir şekilde açmanın çeşitli yollarını kullanmaya devam edebilirsiniz - ancak bunları her zaman dikkatli kullanmalısınız.
Opsiyonlarla nasıl güvenle başa çıkabilirim?
İsteğe bağlı bir değer içerip içermediğini kontrol etmenin en basit yolu, onu karşılaştırmaktır nil
.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
Bununla birlikte, isteğe bağlı seçeneklerle çalışırken% 99,9 oranında, içerdiği değere, bir tane içeriyorsa, aslında erişmek isteyeceksiniz. Bunu yapmak için, İsteğe Bağlı Bağlamayı kullanabilirsiniz .
İsteğe Bağlı Bağlama
İsteğe Bağlı Bağlama, isteğe bağlı bir değer içerip içermediğini kontrol etmenize ve sarmalanmamış değeri yeni bir değişkene veya sabite atamanıza olanak tanır. Bu sözdizimi kullanır if let x = anOptional {...}
veya if var x = anOptional {...}
bunu bağlayıcı sonra yeni değişkenin değerini değiştirmek gerekiyorsa, bağlı.
Ö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ı öğenin bir değer içerip içermediğini 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 güzel olan şey, aynı anda birden fazla isteğe bağlı seçeneği açabilmenizdir. İfadeleri virgülle ayırabilirsiniz. Tüm opsiyonlar açılmazsa 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 numara da, sarmayı açtıktan sonra değerdeki 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 ifadesi içinde isteğe bağlı bağlamanın kullanılmasıyla ilgili tek sorun, sarılmamış değere yalnızca ifadenin kapsamından erişebilmenizdir. İfadenin kapsamının dışından değere erişmeniz gerekiyorsa, bir koruma ifadesi kullanabilirsiniz .
Bir guard deyimi , başarı için bir koşul tanımlamanıza izin verir ve mevcut kapsam yalnızca bu koşul karşılanırsa yürütülmeye devam eder. Sözdizimi ile tanımlanırlar guard condition else {...}
.
Dolayısıyla, bunları isteğe bağlı bir bağlama ile kullanmak için şunu yapabilirsiniz:
guard let number = anOptionalInt else {
return
}
(Koruma organı içinde, o anda yürütülen kodun kapsamından çıkmak için kontrol aktarım ifadelerinden birini kullanmanız gerektiğini unutmayın ).
Eğer anOptionalInt
bir değer içerir, bu Çizelgesi ve yeni atanmış olacak number
sabiti. Korumadan sonraki kod daha sonra çalışmaya devam edecektir. Bir değer içermiyorsa, koruma parantez içindeki kodu çalıştıracak ve bu da kontrolün aktarılmasına yol açacaktır, böylece hemen sonraki kod çalıştırılmayacaktır.
Guard deyimleriyle ilgili gerçek düzgün şey, sarmalanmamış değerin artık ifadeyi izleyen kodda kullanılabilir olmasıdır (çünkü gelecekteki kodun yalnızca isteğe bağlı bir değer varsa çalıştırılabileceğini biliyoruz ). Bu, birden çok if ifadesinin iç içe geçmesiyle oluşturulan 'kıyamet piramitlerini' ortadan kaldırmak için harikadır .
Örneğin:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
Muhafızlar ayrıca, birden çok isteğe bağlı seçeneği aynı anda açmak ve tümceyi kullanmak gibi, if ifadesinin desteklediği aynı düzgün hileleri destekler where
.
Bir if veya guard ifadesi kullanıp kullanmayacağınız tamamen 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ürdedir a
(genellikle isteğe bağlı olmasa da).
Esasen " a
Bir değer içeriyorsa, onu açın. Aksi takdirde geri dönülmez b
”. Örneğin, bunu şu şekilde kullanabilirsiniz:
let number = anOptionalInt ?? 0
Bu , bir değer içeriyorsa veya başka türlü değerini içerecek bir tür number
sabitini tanımlayacaktır .Int
anOptionalInt
0
Sadece kısaltması:
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'yi kullanabilirsiniz . Bu, basitçe, değişken isminin, ?
onu kullanırken bir ile sonlandırmasıyla 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?
foo
Hiçbir şey döndürmeyen bir yöntem çağırmak istersek, basitçe yapabiliriz:
foo?.doSomethingInteresting()
Eğer foo
bir değer içerir, bu yöntem üzerinde çağrılır. Olmazsa, kötü bir şey olmayacak - kod basitçe çalışmaya devam edecektir.
(Bu, nil
Objective-C'de mesaj göndermeye benzer bir davranıştır )
Bu nedenle bu, özellikleri ve çağrı yöntemlerini ayarlamak için de kullanılabilir. Örneğin:
foo?.bar = Bar()
Yine, eğer öyleyse foo
, burada kötü bir şey olmayacak nil
. Kodunuz çalışmaya devam edecek.
İsteğe bağlı zincirlemenin yapmanıza izin verdiği bir başka güzel numara, bir özelliği 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önmesidir)
Örneğin:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
Ancak, özelliklere erişmeye veya bir değer döndüren yöntemleri çağırmaya çalışırken işler biraz daha karmaşık hale gelir. Çünkü foo
isteğe bağlıdır, ondan dönen şey opsiyonel olacak. Bunun üstesinden gelmek için, yukarıdaki yöntemlerden birini kullanarak döndürülen opsiyonların sarmalını kaldırabilir veya foo
yöntemlere erişmeden veya değerleri döndüren yöntemleri çağırmadan önce kendi kendini 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, döndürülen değer, kendisinin isteğe bağlı qux
olup olmadığına bakılmaksızın her zaman isteğe qux
bağlı olacaktır.
map
ve flatMap
Opsiyonellerde sıklıkla yetersiz kullanılan bir özellik, map
ve flatMap
işlevlerini kullanma yeteneğidir . Bunlar, isteğe bağlı değişkenlere isteğe bağlı olmayan dönüşümler uygulamanıza izin verir. İsteğe bağlı bir değer varsa, ona belirli bir dönüşümü uygulayabilirsiniz. Bir değeri yoksa, kalacaktır nil
.
Örneğin, isteğe bağlı bir dizeniz olduğunu varsayalım:
let anOptionalString:String?
map
İşlevi ona uygulayarak - stringByAppendingString
onu başka bir dizgeyle birleştirmek için işlevi 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 kullanılmasına izin verebiliriz 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 geri 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
olarak geri dönmenize izin vermesi dışında, benzer şekilde çalışır . Bu, isteğe bağlı olmayan bir giriş gerektiren bir işleme isteğe bağlı bir giriş yapabileceğiniz, ancak isteğe bağlı bir çıktı verebileceğ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 şekilde catch
bloğa yakalanır .
error
Eğer gördüğünüz sabit catch
bloğu tarafımızca beyan edilmiş - otomatik araya getirilen catch
.
error
Kendinizi de ilan edebilirsiniz , onu yararlı bir formata çevirebilme 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ğıdakilerle "denemeye zorlamak" için de bir yol sağlar try!
:
let result = try! someThrowingFunc()
Bu yazıda açıklanan kavramlar burada da geçerlidir: Bir hata atılırsa, uygulama çökecektir.
Yalnızca try!
sonucun sizin bağlamınızda asla başarısız olmayacağını ispatlayabilirseniz kullanmalısınız - ve bu çok nadirdir.
Çoğu zaman eksiksiz Do-Try-Catch sistemini ve isteğe bağlı olanı, try?
hatayı işlemenin önemli olmadığı nadir durumlarda kullanacaksınız .
Kaynaklar
TL; DR yanıtı
İle çok az istisna dışında , bu kural altındır:
Kullanmaktan kaçının !
Değişken isteğe bağlı ( ?
), örtük olarak kapatılmamış isteğe bağlı (IUO) ( !
) bildirme
Başka bir deyişle, şunu kullanın:
var nameOfDaughter: String?
Onun yerine:
var nameOfDaughter: String!
İsteğe bağlı değişkeni if let
veya kullanarak açınguard let
Değişkeni şu şekilde aç:
if let nameOfDaughter = nameOfDaughter {
print("My daughters name is: \(nameOfDaughter)")
}
Veya bunun gibi:
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")
Bu cevabın kısa olması amaçlanmıştır, tam kavrayış için kabul edilen cevabı okuyun
Kaynaklar
Bu soru SO'da HER ZAMAN ortaya çıkıyor . Yeni Swift geliştiricilerinin uğraştığı ilk şeylerden biri.
Arka fon:
Swift, bir değer içerebilen veya içermeyen değerlerle başa çıkmak için "Opsiyonel" kavramını kullanır. C gibi diğer dillerde, hiçbir değer içermediğini belirtmek için bir değişkende 0 değerini depolayabilirsiniz. Ancak, 0 geçerli bir değerse ne olur? O zaman -1 kullanabilirsiniz. Ya -1 geçerli bir değerse? Ve bunun gibi.
Swift opsiyonları, geçerli bir değer içerecek veya değer içermeyecek herhangi bir türde değişken ayarlamanıza izin verir.
Bir değişkeni anlama gelecek şekilde ilan ettiğinizde (x yazın veya değer yok) türünden sonra bir soru işareti koyarsınız.
Bir isteğe bağlı, aslında ya belirli bir türden bir değişken içeren ya da hiçbir şey içermeyen bir kaptır.
İçerideki değeri almak için isteğe bağlı bir "sarmalanmamış" olması gerekir.
"!" işleci bir "sarmaya zorla" operatörüdür. "Bana güven. Ne yaptığımı biliyorum. Bu kod çalıştığında değişkenin sıfır içermeyeceğini garanti ederim." Diyor. Eğer yanılıyorsan, düşersin.
Gerçekten sürece do ne yaptığınızı biliyorum, kaçının "!" zorla açma operatörü. Swift programcılarına yeni başlayanlar için muhtemelen en büyük kilitlenme kaynağıdır.
Opsiyonlarla nasıl başa çıkılır:
Opsiyonellerle başa çıkmanın daha güvenli birçok yolu vardır. İşte bazıları (kapsamlı bir liste değil)
Bu isteğe bağlı bir değer içeriyorsa "isteğe bağlı bağlama" veya "izin verirse" demek için "kullanabilirsiniz, bu değeri yeni, isteğe bağlı olmayan bir değişkene kaydedin. İsteğe bağlı bir değer içermiyorsa, bu if ifadesinin gövdesini atlayın ".
İsteğe bağlı bağlamaya ilişkin bir örnek aşağıda verilmiştir foo
:
if let newFoo = foo //If let is called optional binding. {
print("foo is not nil")
} else {
print("foo is nil")
}
İsteğe bağlı teklif verme kullandığınızda tanımladığınız değişkenin if ifadesinin gövdesinde yalnızca (yalnızca "kapsam dahilinde") bulunduğunu unutmayın.
Alternatif olarak, değişken nil ise işlevinizden çıkmanıza izin veren bir guard deyimi kullanabilirsiniz:
func aFunc(foo: Int?) {
guard let newFoo = input else { return }
//For the rest of the function newFoo is a non-optional var
}
Güvenlik ifadeleri Swift 2'ye eklendi. Guard, kodunuz aracılığıyla "altın yolu" korumanıza ve bazen "if let" isteğe bağlı bağlamanın kullanılmasından kaynaklanan, sürekli artan iç içe geçmiş durumlardan kaçınmanıza olanak tanır.
Ayrıca "sıfır birleştirme operatörü" adı verilen bir yapı da vardır. Bu, "isteğe bağlı_var" değiştirme_değer "biçimini alır. İsteğe bağlı içinde bulunan verilerle aynı türde isteğe bağlı olmayan bir değişken döndürür. İsteğe bağlı nil içeriyorsa, "??" işaretinden sonra ifadenin değerini döndürür. sembol.
Böylece kodu şu şekilde kullanabilirsiniz:
let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")
Try / catch veya guard hata işlemeyi de kullanabilirsiniz, ancak genellikle yukarıdaki diğer tekniklerden biri daha temizdir.
DÜZENLE:
İsteğe bağlı bir başka, biraz daha incelikli bir şey, "örtük olarak açılmamış isteğe bağlı öğelerdir. Foo'yu ilan ettiğimizde, şunu söyleyebiliriz:
var foo: String!
Bu durumda foo hala isteğe bağlıdır, ancak başvurmak için onu açmanız gerekmez. Bu, ne zaman foo'ya başvurmaya çalışsanız, sıfırsa çökersiniz demektir.
Yani bu kod:
var foo: String!
let upperFoo = foo.capitalizedString
Foo'yu açmaya zorlamasak bile, foo'nun capitalizedString özelliğine referansla çökecek. baskı iyi görünüyor, ama değil.
Bu nedenle örtük olarak açılmamış opsiyonlara gerçekten dikkat etmek istersiniz. (ve belki de isteğe bağlı olarak sağlam bir anlayışa sahip olana kadar onlardan tamamen kaçının.)
Alt satır: Swift'i ilk öğrenirken, "!" karakter dilin bir parçası değildir. Başını belaya sokması muhtemel.
Yukarıdaki cevaplar Opsiyonel ile nasıl güvenli bir şekilde oynanacağını açıkça açıkladığından. Opsiyonların gerçekte ne olduğunu hızlıca açıklamaya çalışacağım.
İsteğe bağlı bir değişkeni bildirmenin başka bir yolu da
var i : Optional<Int>
İsteğe bağlı tür, iki durumlu bir numaralandırmadan başka bir şey değildir, yani
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
Yani 'i' değişkenimize bir sıfır atamak için. Yapabiliriz
var i = Optional<Int>.none
veya bir değer atayabiliriz, bir değer aktarırız
var i = Optional<Int>.some(28)
Swift'e göre 'sıfır', değerin olmamasıdır. Ve ile başlatılan bir örnek oluşturmak için, tahmin ederseniz harika olarak nil
adlandırılan bir protokole uymak ExpressibleByNilLiteral
zorundayız, yalnızca diğer türlere Optionals
uymak ExpressibleByNilLiteral
ve uymak tavsiye edilmez.
ExpressibleByNilLiteral
init(nilLiteral:)
nil ile bir örneği başlatan tek bir metoda sahiptir . Genellikle bu yöntemi çağırmayacaksınız ve hızlı belgelere göre, bu başlatıcıyı doğrudan bir İsteğe bağlı türü nil
hazır değerle başlattığınızda derleyici çağırdığı için doğrudan çağırmanız önerilmez .
Kendim bile kafamı Opsiyonel: D Happy Swfting All'a sarmak zorunda (kelime anlamı yok) .
Öncelikle, İsteğe Bağlı bir değerin ne olduğunu bilmelisiniz. Ayrıntılar için Swift Programlama Diline geçebilirsiniz .
İkinci olarak, isteğe bağlı değerin iki durumu olduğunu bilmelisiniz. Biri tam değerdir ve diğeri sıfır değerdir. Bu nedenle, isteğe bağlı bir değer uygulamadan önce, hangi durumda olduğunu kontrol etmelisiniz.
Sen kullanabilir if let ...
veya guard let ... else
vb.
Bir başka yol, uygulamanızdan önce değişken durumunu kontrol etmek istemiyorsanız, var buildingName = buildingName ?? "buildingName"
bunun yerine kullanabilirsiniz .
Çıkış değerlerimi segue için hazırlama yönteminden aşağıdaki gibi ayarlamaya çalışırken bir kez bu hatayı yaşadım:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// This line pops up the error
destination.nameLabel.text = item.name
}
}
}
Sonra, hedef denetleyici çıkışlarının değerlerini ayarlayamadığımı öğrendim çünkü denetleyici henüz yüklenmemiş veya başlatılmamış.
Ben de şu şekilde çözdüm:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// Created this method in the destination Controller to update its outlets after it's being initialized and loaded
destination.updateView(itemData: item)
}
}
}
Hedef Denetleyicisi:
// This variable to hold the data received to update the Label text after the VIEW DID LOAD
var name = ""
// Outlets
@IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
nameLabel.text = name
}
func updateView(itemDate: ObjectModel) {
name = itemDate.name
}
Umarım bu cevap, işaretli cevabın opsiyonellerin ve nasıl çalıştıklarının anlaşılması için harika bir kaynak olduğunu, ancak sorunun kendisini doğrudan ele almadığını fark ettiğimle aynı sorunu yaşayan herkese yardımcı olur.
Temel olarak, Swift'in yalnızca sıfır olmayan değerlere izin verdiği yerlerde, derleyiciye orada hiçbir zaman sıfır değer olmayacağına güvenmesini söyleyerek, böylece uygulamanızın derlenmesine izin vererek bir nil değeri kullanmaya çalıştınız.
Bu tür ölümcül hataya yol açan birkaç senaryo vardır:
zorla açmalar:
let user = someVariable!
Eğer
someVariable
sıfır ise, bir kilitlenme yaşarsınız. Zorla açmayı yaparak, sıfır denetim sorumluluğunu derleyiciden size taşıdınız, temelde zorunlu bir açmama yaparak derleyiciye orada hiçbir zaman sıfır değerlere sahip olmayacağınızı garanti ediyorsunuz. Ve bir şekilde sıfır değeri ile biterse ne olur tahmin edinsomeVariable
?Çözüm? İsteğe bağlı bağlama (diğer adıyla if-let) kullanın, değişken işlemeyi orada yapın:
if user = someVariable { // do your stuff }
zorla (aşağı) yayınlar:
let myRectangle = someShape as! Rectangle
Burada zorla döküm ile derleyiciye artık endişelenmemesini söylersiniz, çünkü her zaman
Rectangle
orada bir örneğiniz olacaktır. Ve bu geçerli olduğu sürece endişelenmenize gerek yok. Sorunlar, siz veya projedeki meslektaşlarınız dikdörtgen olmayan değerleri dolaştırmaya başladığında başlar.Çözüm? İsteğe bağlı bağlama (diğer adıyla if-let) kullanın, değişken işlemeyi orada yapın:
if let myRectangle = someShape as? Rectangle { // yay, I have a rectangle }
Örtülü olarak açılmamış isteğe bağlı öğeler. Aşağıdaki sınıf tanımına sahip olduğunuzu varsayalım:
class User { var name: String! init() { name = "(unnamed)" } func nicerName() { return "Mr/Ms " + name } }
Şimdi, hiç kimse
name
özelliği ayarlayarak özelliği karıştırmazsanil
, beklendiği gibi çalışır, ancak anahtarsızUser
bir JSON'dan başlatılırsaname
, özelliği kullanmaya çalışırken ölümcül hatayı alırsınız.Çözüm? Bunları kullanmayın :) Özelliğin kullanılması gerektiği zamana kadar her zaman sıfır olmayan bir değere sahip olacağından% 102 emin değilseniz. Çoğu durumda isteğe bağlı veya isteğe bağlı olmayana dönüştürme işe yarayacaktır. Bunun isteğe bağlı olmaması, derleyicinin kaçırdığınız kod yollarını söyleyerek o mülke bir değer vermesini sağlayarak size yardımcı olur.
Bağlı olmayan veya henüz bağlanmamış prizler. Bu, 3 numaralı senaryonun özel bir durumudur. Temel olarak kullanmak istediğiniz XIB yüklü bir sınıfınız var.
class SignInViewController: UIViewController { @IBOutlet var emailTextField: UITextField! }
Şimdi prizi XIB düzenleyiciden bağlamayı kaçırdıysanız, prizi kullanmak isteyeceğiniz anda uygulama çökecektir. Çözüm? Tüm çıkışların bağlı olduğundan emin olun. Veya kullanmak
?
onlara operatörünü:emailTextField?.text = "[email protected]"
. Veya çıkışı isteğe bağlı olarak bildirin, ancak bu durumda derleyici sizi kodun her yerine sarmaya zorlayacaktır.Objective-C'den gelen ve null atanabilirlik ek açıklamaları olmayan değerler. Aşağıdaki Objective-C sınıfına sahip olduğumuzu varsayalım:
@interface MyUser: NSObject @property NSString *name; @end
Şimdi hiçbir boş değer atanabilirlik ek açıklaması belirtilmezse (açıkça veya
NS_ASSUME_NONNULL_BEGIN
/ aracılığıylaNS_ASSUME_NONNULL_END
),name
özellik Swift'eString!
(bir IUO - örtük olarak açılmamış isteğe bağlı) olarak içe aktarılacaktır . Bazı hızlı kodlar değeri kullanmak isteyeceği andaname
, sıfır ise çökecektir.Çözüm? Objective-C kodunuza boş değer atanabilirlik ek açıklamaları ekleyin. Bununla birlikte, Objective-C derleyicisi, boş değer atanabilirlik söz konusu olduğunda biraz izin vericidir, bunları açıkça işaretlemiş olsanız bile, sıfır değerlerle sonuçlanabilirsiniz
nonnull
.
Bu daha önemli bir yorumdur ve nil
değerlerin hata ayıklaması söz konusu olduğunda neden örtük olarak sarmalanmamış isteğe bağlı ifadeler aldatıcı olabilir .
Aşağıdaki kodu düşünün: Hata / uyarı olmadan derler:
c1.address.city = c3.address.city
Yine de çalışma zamanında şu hatayı veriyor: Önemli hata: İsteğe bağlı bir değeri açarken beklenmedik bir şekilde sıfır bulundu
Bana hangi nesnenin olduğunu söyleyebilir misin nil
?
Yapamazsın!
Tam kod şöyle olacaktır:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c3 = BadContact()
c1.address.city = c3.address.city // compiler hides the truth from you and then you sudden get a crash
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct BadContact {
var address : Address!
}
struct Address {
var city : String
}
Kullanarak Uzun lafın kısası var address : Address!
sen ediyoruz gizleme bir değişken olabilir ihtimalini nil
diğer okuyuculardan. Ve çöktüğünde, "ne halt ediyorsun ?! benim address
isteğe bağlı değil, öyleyse neden çöküyorum?!.
Bu nedenle şöyle yazmak daha iyidir:
c1.address.city = c2.address!.city // ERROR: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Şimdi bana bunun hangi nesne olduğunu söyleyebilir misin nil
?
Bu sefer kod size daha açık hale getirildi. Rasyonelleştirebilir ve muhtemelen address
zorla açılmış olan parametrenin bu olduğunu düşünebilirsiniz .
Tam kod şöyle olacaktır:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c2 = GoodContact()
c1.address.city = c2.address!.city
c1.address.city = c2.address?.city // not compile-able. No deceiving by the compiler
c1.address.city = c2.address.city // not compile-able. No deceiving by the compiler
if let city = c2.address?.city { // safest approach. But that's not what I'm talking about here.
c1.address.city = city
}
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct GoodContact {
var address : Address?
}
struct Address {
var city : String
}
Hatalar EXC_BAD_INSTRUCTION
ve fatal error: unexpectedly found nil while implicitly unwrapping an Optional value
en çok bir ilan ettiğinizde @IBOutlet
, ancak film şeridine bağlı olmadığınızda görünür .
Diğer cevaplarda bahsedilen Seçeneklerin nasıl çalıştığını da öğrenmelisiniz , ancak bu bana çoğunlukla görünen tek zamandır.
CollectionView'da bu hatayı alırsanız, CustomCell dosyası ve Özel xib de oluşturmayı deneyin.
bu kodu mainVC'de ViewDidLoad () 'a ekleyin.
let nib = UINib(nibName: "CustomnibName", bundle: nil)
self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
Bir tablo görünümü denetleyicisinden bir görünüm denetleyicisine bir geçiş yaparken bu hatayla karşılaştım çünkü ana film şeridinde görünüm denetleyicisi için özel sınıf adını belirtmeyi unutmuştum.
Her şey yolunda görünüyorsa kontrol etmeye değer basit bir şey