Hızlı yap-dene-yakala sözdizimi


162

Ben hızlı 2 yeni bir hata işleme şey anlamak için bir deneyin. İşte yaptım: İlk önce bir hata enum ilan:

enum SandwichError: ErrorType {
    case NotMe
    case DoItYourself
}

Ve sonra bir hata atan bir yöntem (istisna millet değil. Bu bir hatadır.) İlan ettim. İşte bu yöntem:

func makeMeSandwich(names: [String: String]) throws -> String {
    guard let sandwich = names["sandwich"] else {
        throw SandwichError.NotMe
    }

    return sandwich
}

Sorun çağıran tarafından. Bu yöntemi çağıran kod şöyledir:

let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
}

Sonra doderleyici derler diyor Errors thrown from here are not handled because the enclosing catch is not exhaustive. Ama bence bu çok kapsamlı çünkü SandwichErrorenum'da sadece iki vaka var .

Düzenli anahtar ifadeleri için swift, her durum ele alındığında bunun kapsamlı olduğunu anlayabilir.



Hata türünü belirtmenin bir yolu var mı?
mustafa

Swift kitabının yeni sürümünde hiçbir şey bulamıyorum - şu anda yalnızca atar anahtar kelimesi
Farlei Heinen

Hatalar veya uyarılar olmadan bir oyun alanında benim için çalışıyor.
Fogmeister

2
Oyun alanları do, en üst düzeyde kapsamlı olmayan bloklara izin veriyor gibi görünüyor - eğer atmayı atmayan bir fonksiyonda sararsanız hata oluşacaktır.
Sam

Yanıtlar:


267

Swift 2 hata işleme modelinin iki önemli noktası vardır: kapsamlılık ve esneklik. Birlikte, sadece atabileceğinizi bildiğinizleri değil, olası her hatayı yakalamanız gereken do/ catchifadenize kaybolurlar.

Bir işlevin ne tür hatalar atabileceğini, yalnızca atıp atmadığını bildirmediğinize dikkat edin. Bu sıfır bir sonsuzluk problemidir: başkalarının (gelecekteki benliğiniz dahil) kullanması için bir işlev tanımlayan biri olarak, işlevinizin her istemcisinin, uygulamanızdaki her değişikliğe adapte olmasını istemezsiniz. hangi hatalar atabileceğini de içerir. İşlevinizi çağıran kodun bu değişikliğe dayanıklı olmasını istiyorsunuz.

İşleviniz ne tür hatalar attığını (veya gelecekte atabileceğini) söyleyemediğinden, hataları catchyakalayan bloklar ne tür hatalar atabileceğini bilemez. Bu nedenle, bildiğiniz hata türlerini ele catchalmanın yanı sıra, evrensel bir ifadeyle yapmadığınız durumları da ele almanız gerekir - bu şekilde işleviniz gelecekte atacağı hata kümesini değiştirirse, arayanlar yine de hatalar.

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
} catch let error {
    print(error.localizedDescription)
}

Ama burada durmayalım. Bu esneklik fikrini biraz daha düşünün. Sandviçinizi tasarladığınız şekilde, hataları kullandığınız her yerde tanımlamanız gerekir. Bu, hata durumları kümesini her değiştirdiğinizde, bunları kullanan her yeri değiştirmek zorunda olduğunuz anlamına gelir ... çok eğlenceli değil.

Kendi hata türlerinizi tanımlamanın ardındaki fikir, bunun gibi şeyleri merkezileştirmenize izin vermektir. descriptionHatalarınız için bir yöntem tanımlayabilirsiniz :

extension SandwichError: CustomStringConvertible {
    var description: String {
        switch self {
            case NotMe: return "Not me error"
            case DoItYourself: return "Try sudo"
        }
    }
}

Ve sonra hata işleme kodunuz hata türünüzün kendisini tanımlamasını isteyebilir - şimdi hataları ele aldığınız her yer aynı kodu kullanabilir ve gelecekteki olası hata durumlarını da işleyebilir.

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch let error as SandwichError {
    print(error.description)
} catch {
    print("i dunno")
}

Bu ayrıca hata türlerinin (veya üzerlerindeki uzantıların) hataları bildirmenin diğer yollarını desteklemesinin yolunu açar - örneğin, UIAlertControllerhata türünüzde hatayı bir iOS kullanıcısına bildirmek için nasıl sunulacağını bilen bir uzantıya sahip olabilirsiniz .


1
@rickster: Derleyici hatasını gerçekten yeniden üretebilir misiniz? Orijinal kod benim için herhangi bir hata veya uyarı olmadan derleniyor. Ve eğer yakalanmamış bir istisna atılırsa, program durur. - Söylediğin error caught in main()her şey kulağa mantıklı gelse de, bu davranışı yeniden üretemem .
Martin R

5
Bir uzantıdaki hata mesajlarını nasıl ayırdığınızı sevin. Kodunuzu temiz tutmanın gerçekten güzel bir yolu! Harika bir örnek!
Konrad77

tryBir çalışma zamanı hatasına neden olabileceği ve uygulamanızın çökmesine neden olabileceğinden, üretim kodundaki zorunlu ifadeyi kullanmamanız önemle önerilir
Otar

@ Genel olarak iyi düşünülmüş, ancak biraz konu dışı - cevap kullanarak (veya kullanmayla) ilgilenmiyor try!. Ayrıca, Swift'teki çeşitli "kuvvet" operasyonları için üretim kodu için bile geçerli, "güvenli" kullanım durumları vardır - önkoşul veya yapılandırma yoluyla arıza olasılığını güvenilir bir şekilde ortadan kaldırırsanız, kısa devre için kısa devre için test edilemeyen hata işleme kodunu yazmaktan daha mantıklı olmak.
rickster

İhtiyacınız olan tek şey hata mesajını görüntülemekse, bu mantığı SandwichErrorsınıfın içine koymak mantıklıdır. Ancak, çoğu hata için şüpheli, hata işleme mantığı çok kapsüllenmiş olamaz. Bunun nedeni genellikle arayanın bağlamı hakkında bilgi gerektirmesidir (geri yükleme veya yeniden deneme veya yukarı doğru bir arıza bildirme vb.). Başka bir deyişle, en yaygın modelin yine de belirli hata türleriyle eşleşmesi gerektiğinden şüpheleniyorum.
maksimum

29

Bunun henüz tam olarak uygulanmadığından şüpheleniyorum. Swift Programlama Kılavuzu kesinlikle derleyici 'bir switch deyimi gibi' kapsamlı eşleşmeleri çıkarabiliriz ima etmek gibi görünüyor. catchKapsamlı olabilmek için bir generale ihtiyaç duyulduğundan bahsetmez .

Ayrıca hatanın trysatırın sonunda değil satırda olduğunu fark edersiniz , yani bir noktada derleyici trybloktaki hangi ifadenin işlenmeyen istisna türlerine sahip olduğunu saptayabilir .

Belgeler biraz belirsiz. 'Swift'teki yenilikler' videosunu gözden geçirdim ve hiçbir ipucu bulamadım; Denemeye devam edeceğim.

Güncelleme:

Artık Beta 3'e kadar ErrorType çıkarımının ipucu yok. Şimdi bunun planlanmış olup olmadığına inanıyorum (ve hala bir noktada olduğunu düşünüyorum), protokol uzantıları üzerindeki dinamik gönderim muhtemelen onu öldürdü.

Beta 4 Güncellemesi:

Xcode 7b4 Throws:, “hangi hataların atılabileceğini ve nedenini belgelemek için kullanılması gereken” doküman yorumu desteği ekledi . Sanırım bu en azından API tüketicilerine hataları iletmek için bazı mekanizmalar sağlar . Belgeleriniz olduğunda kimin bir tip sisteme ihtiyacı var!

Başka bir güncelleme:

Otomatik için umut biraz zaman geçirdikten sonra ErrorTypeçıkarım ve sınırlamalar bu modelin ne olacağını çalışma dışarı, ben fikrimi değiştirdim - Bu yerine elma uygular umut budur. esasen:

// allow us to do this:
func myFunction() throws -> Int

// or this:
func myFunction() throws CustomError -> Int

// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int

Yine Bir Güncelleme

Apple'ın hata işleme gerekçesi artık burada . Hızlı evrim posta listesinde bazı ilginç tartışmalar da oldu . Esasen, John McCall yazılan hatalara karşıdır, çünkü çoğu kütüphanenin yine de genel bir hata durumu da dahil olacağına inanmaktadır ve yazılan hataların, kazan plakası dışında koda çok fazla şey eklemesi olası değildir ('aspirasyon blöf' terimini kullandı). Chris Lattner, esneklik modeliyle çalışabiliyorsa Swift 3'te yazılan hatalara açık olduğunu söyledi.


Bağlantılar için teşekkürler. Ancak John tarafından ikna edilmedi: "birçok kütüphane" diğer hata "türünü içerir", herkesin "diğer hata" türüne ihtiyacı olduğu anlamına gelmez.
Franklin Yu

Açık olan sayaç, bir işlevin ne tür bir hata atacağını bilmenin basit bir yolunun bulunmasına kadar, geliştiriciyi tüm hataları yakalamaya ve bunları olabildiğince iyi ele almaya çalışmasıdır. Dürüst olmak gerekirse, oldukça can sıkıcı bir durum.
William T Froggard

4

Swift, vaka ifadenizin tüm vakaları kapsamadığından endişe duyuyor, düzeltmek için varsayılan bir vaka oluşturmanız gerekiyor:

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
} catch Default {
    print("Another Error")
}

2
Ama bu garip değil mi? Sadece iki vakam var ve hepsi catchifadelerde listeleniyor .
mustafa

2
Şimdi ek bir geliştirme isteği için iyi bir zaman func method() throws(YourErrorEnum), hatta throws(YourEnum.Error1, .Error2, .Error3)ne atılabileceğini biliyorsun
Matthias Bauch

8
WWDC oturumlarından birindeki Swift derleyici ekibi, 'Java gibi' olası tüm hataların bilgiçlik listelerini istemediklerini açıkça belirtti.
Sam

4
Varsayılan / varsayılan hata yoktur; diğer posterlerin işaret ettiği gibi boş bir catch {} bırakın
Opus1217

1
@Icaro Bu beni güvende hissetmiyor; ileride "diziye yeni bir girdi eklerim" durumunda, derleyici etkilenen tüm catch koşullarını güncellemediğim için bana bağırmalıdır.
Franklin Yu

3

Ayrıca, bir fonksiyonun atabileceği tipin olmaması nedeniyle hayal kırıklığına uğradım, ancak şimdi @rickster sayesinde aldım ve bunu şöyle özetleyeceğim: diyelim ki bir fonksiyonun attığı türü belirleyebiliriz, böyle bir şeyimiz olurdu:

enum MyError: ErrorType { case ErrorA, ErrorB }

func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }

do {
    try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }

Sorun şu ki, myFunctionThatThrows içinde hiçbir şey değiştirmesek bile, MyError'a bir hata durumu eklersek:

enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }

vidalıyız çünkü do / try / catch'ımız artık kapsamlı değil, aynı zamanda MyError'u atan işlevler olarak adlandırdığımız başka bir yer


3
Neden vidalandığını takip ettiğimden emin değilim. Bir derleyici hatası alırdınız, bu da istediğiniz şey değil mi? Bir numaralandırma durumu eklerseniz ifadeleri değiştirmek için olan budur.
Sam

Bir anlamda bana bunun hata enums / do davasında olacağı büyük olasılıkla görünüyordu, ancak tam olarak enums / switch'de olduğu gibi, haklısınız. Hala Apple'ın attığımız şeyi yazmama tercihinin iyi olduğunu ikna etmeye çalışıyorum, ama bana bu konuda yardım etmiyorsun! ^^
greg3z

Atılan hataları manuel olarak yazmak önemsiz durumlarda büyük bir karmaşaya dönüşür. Türler, işlev içindeki tüm at ve dene ifadelerinden kaynaklanabilecek tüm hataların birleşimidir. Hata numaralarını elle koruyorsanız, bu acı verici olacaktır. catch {}Her bloğun altındaki A tartışmalı olarak daha kötüdür. Derleyici sonunda nerede olabilir hata türlerini otomatik olarak çıkarım umuyoruz ama onaylamak mümkün değil.
Sam

Derleyicinin teorik olarak bir işlevin attığı hata türlerini çıkartabilmesi gerektiğine katılıyorum. Ama bence geliştiricilerin bunları netleştirmek için açıkça yazmaları da mantıklı. Bahsettiğiniz önemsiz durumlarda, farklı hata türlerini listelemek bana iyi geliyor: func f (), ErrorTypeA, ErrorTypeB {}
greg3z

Hata türlerini (doc yorumları dışında) iletecek bir mekanizmanın bulunmaması kesinlikle eksiktir. Bununla birlikte, Swift ekibi, hata türlerinin açık listelerini istemediklerini söyledi. Eminim geçmişte Java tarafından kontrol edilen istisnalarla uğraşan çoğu insan aynı fikirde olacaktır
Sam

1
enum NumberError: Error {
  case NegativeNumber(number: Int)
  case ZeroNumber
  case OddNumber(number: Int)
}

extension NumberError: CustomStringConvertible {
         var description: String {
         switch self {
             case .NegativeNumber(let number):
                 return "Negative number \(number) is Passed."
             case .OddNumber(let number):
                return "Odd number \(number) is Passed."
             case .ZeroNumber:
                return "Zero is Passed."
      }
   }
}

 func validateEvenNumber(_ number: Int) throws ->Int {
     if number == 0 {
        throw NumberError.ZeroNumber
     } else if number < 0 {
        throw NumberError.NegativeNumber(number: number)
     } else if number % 2 == 1 {
         throw NumberError.OddNumber(number: number)
     }
    return number
}

Şimdi Doğrulama Numarası:

 do {
     let number = try validateEvenNumber(0)
     print("Valid Even Number: \(number)")
  } catch let error as NumberError {
     print(error.description)
  }

-2

Bunun gibi numaralandırma oluşturun:

//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}

Şunun gibi bir yöntem oluşturun:

 func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
    throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
    throw spendingError.limit
}
return morningSpending + eveningSpending
}

Şimdi hata olup olmadığını kontrol edin ve halledin:

do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}

Yakın ama puro yok. Boşluğu sabitlemeyi ve numaralandırma deve kasasını yapmayı deneyin.
Alec
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.