"Önemli hata: İsteğe bağlı bir değeri açarken beklenmedik şekilde sıfır bulundu" ne anlama geliyor?

Aug 24 2015

Swift programım çöküyor EXC_BAD_INSTRUCTIONve 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

685 Hamish Aug 24 2015 at 02:10

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 .noneeş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 , Intyanlışlıkla geçmenizi engeller . Aynı şekilde, tür güvenliği, isteğe bağlı Stringolanı yanlışlıkla isteğe bağlı olmayan bir kod parçasına geçirmenizi engeller String. 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 intiçin boş gösterici kullanılamaz, bu nedenle ayrı bir değişkene ( value: Intve gibi isValid: Bool) veya belirlenmiş bir gözlemci değere ( -1veya gibi INT_MIN) ihtiyacınız olacaktır. Bu yaklaşımlar hataya açıktır çünkü isValidsentinel 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 nildeğ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 anOptionalStringolduğu gibi , nilonu 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 numberdışı 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 anOptionalIntbir değer içerir, bu Çizelgesi ve yeni atanmış olacak numbersabiti. 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 aisteğe bağlı bir türdür ve baynı türdedir a(genellikle isteğe bağlı olmasa da).

Esasen " aBir 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 numbersabitini tanımlayacaktır .IntanOptionalInt0

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, fooisteğe bağlı bir Fooörnek türünde bir değişkenimiz olduğunu varsayalım.

var foo : Foo?

fooHiçbir şey döndürmeyen bir yöntem çağırmak istersek, basitçe yapabiliriz:

foo?.doSomethingInteresting()

Eğer foobir 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, nilObjective-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 Voiddö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ü fooisteğ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 fooyö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 bazolan isteğe bağlı bir özelliğe sahipse quxaşağıdakileri yazabileceğiniz anlamına gelir :

let optionalQux = foo?.baz?.qux

Yine, foove bazisteğe bağlı olduğundan, döndürülen değer, kendisinin isteğe bağlı quxolup olmadığına bakılmaksızın her zaman isteğe quxbağlı olacaktır.

map ve flatMap

Opsiyonellerde sıklıkla yetersiz kullanılan bir özellik, mapve flatMapiş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 - stringByAppendingStringonu başka bir dizgeyle birleştirmek için işlevi kullanabiliriz .

Çünkü stringByAppendingStringisteğe bağlı olmayan bir string argüman alıyor, doğrudan giriş opsiyonlu dize olamaz. Bununla birlikte, kullanarak map, bir değeri stringByAppendingStringvarsa 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, anOptionalStringbir değeri mapyoksa geri dönecektir nil. Örneğin:

var anOptionalString:String?

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // nil

flatMapkapatma gövdesi içinden başka bir isteğe bağlı mapolarak 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 catchbloğa yakalanır .

errorEğer gördüğünüz sabit catchbloğu tarafımızca beyan edilmiş - otomatik araya getirilen catch.

errorKendinizi 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)
}

tryBu ş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

65 Sajjon Dec 19 2016 at 20:26

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 letveya 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

40 DuncanC Feb 28 2016 at 21:37

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.

13 Tharzeez Nov 17 2017 at 23:15

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 niladlandırılan bir protokole uymak ExpressibleByNilLiteralzorundayız, yalnızca diğer türlere Optionalsuymak ExpressibleByNilLiteralve uymak tavsiye edilmez.

ExpressibleByNilLiteralinit(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ü nilhazı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) .

12 QiunCheng Jul 30 2016 at 14:18

Ö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 ... elsevb.

Bir başka yol, uygulamanızdan önce değişken durumunu kontrol etmek istemiyorsanız, var buildingName = buildingName ?? "buildingName"bunun yerine kullanabilirsiniz .

8 Wissa Apr 13 2018 at 21:06

Çı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.

7 Cristik Sep 19 2018 at 13:04

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:

  1. zorla açmalar:

    let user = someVariable!
    

    Eğer someVariablesı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 edin someVariable?

    Çö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
    }
    
  2. 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 Rectangleorada 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
    }
    
  3. Ö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ırmazsa nil, beklendiği gibi çalışır, ancak anahtarsız Userbir JSON'dan başlatılırsa name, ö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.

  4. 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.

  5. 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ığıyla NS_ASSUME_NONNULL_END), nameözellik Swift'e String!(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 anda name, 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.

2 Honey Nov 01 2018 at 11:39

Bu daha önemli bir yorumdur ve nildeğ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 nildiğer okuyuculardan. Ve çöktüğünde, "ne halt ediyorsun ?! benim addressisteğ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 addresszorla 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
}
2 AleMohamad May 21 2019 at 00:34

Hatalar EXC_BAD_INSTRUCTIONve fatal error: unexpectedly found nil while implicitly unwrapping an Optional valueen ç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.

2 ShigaSuresh Feb 13 2020 at 01:16

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")
MikeVolmar Sep 26 2019 at 22:11

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