Swift hakkında çok fazla şey okumadım ama fark ettiğim bir şey de istisna yok. Peki Swift'te hata işleme nasıl yapıyorlar? Hata işleme ile ilgili bir şey bulan var mı?
Swift hakkında çok fazla şey okumadım ama fark ettiğim bir şey de istisna yok. Peki Swift'te hata işleme nasıl yapıyorlar? Hata işleme ile ilgili bir şey bulan var mı?
Yanıtlar:
Swift 2'de işler biraz değişti, çünkü yeni bir hata işleme mekanizması var, bu da istisnalara biraz daha benzer, ancak ayrıntı bakımından farklı.
İşlev / yöntem bir hata atabileceğini belirtmek istiyorsa, bunun throws
gibi bir anahtar kelime içermelidir
func summonDefaultDragon() throws -> Dragon
Not: İşlevin gerçekten atabileceği hata türü için herhangi bir spesifikasyon yoktur. Bu bildirim basitçe işlevin ErrorType uygulayan herhangi bir türden bir örnek atabileceğini veya hiç atmayacağını belirtir.
İşlevi çağırmak için try anahtar sözcüğünü kullanmanız gerekir, bunun gibi
try summonDefaultDragon()
bu çizgi normalde böyle do-catch bloğu olmalıdır
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}
Not: catch cümlesi, Swift desen eşleşmesinin tüm güçlü özelliklerini kullanın, böylece burada çok esneksiniz.
throws
Anahtar kelime ile işaretlenmiş bir işlevden bir atma işlevi çağırıyorsanız, hatayı yaymaya karar verebilirsiniz :
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
Alternatif olarak, fırlatma fonksiyonunu kullanarak şunları çağırabilirsiniz try?
:
let dragonOrNil = try? summonDefaultDragon()
Bu şekilde, herhangi bir hata oluşursa dönüş değerini veya nil değerini alırsınız. Bu yolla hata nesnesi alamazsınız.
Bu, aşağıdaki try?
gibi yararlı ifadelerle de birleştirebileceğiniz anlamına gelir :
if let dragon = try? summonDefaultDragon()
veya
guard let dragon = try? summonDefaultDragon() else { ... }
Son olarak, hatanın gerçekten gerçekleşmeyeceğini bildiğinize karar verebilirsiniz (örneğin, önceden kontrol ettiğiniz için önkoşullardır) ve try!
anahtar kelime kullanın :
let dragon = try! summonDefaultDragon()
İşlev aslında bir hata atarsa, uygulamanızda bir çalışma zamanı hatası alırsınız ve uygulama sona erer.
Bir hata atmak için böyle bir anahtar kelime kullanın
throw DragonError.dragonIsMissing
ErrorType
Protokole uygun her şeyi atabilirsiniz . Yeni başlayanlar NSError
için bu protokole uygundur, ancak muhtemelen ErrorType
birden fazla ilgili hatayı, potansiyel olarak bunun gibi ek veri parçalarıyla gruplandırmanıza olanak tanıyan enum tabanlı ile gitmek istersiniz.
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
Yeni Swift 2 ve 3 hata mekanizması ile Java / C # / C ++ stili istisnalar arasındaki temel farklar şunlardır:
do-catch
+ try
+ defer
ve geleneksel try-catch-finally
sözdizimine karşı.do-catch
Eğer objc kullanması gerektiğini için blok, tersi herhangi NSException, ve yardımcısı yakalamak olmaz.NSError
metot, ya geri kurallarına false
(için Bool
işlevler dönen) ya da nil
(için AnyObject
işlevler dönen) ve pasNSErrorPointer
hata ayrıntıları ile.Hata işlemeyi kolaylaştırmak için ekstra bir syntatic-şeker olarak, iki kavram daha var
defer
Java / C # / etc'deki bloklarla aynı etkiyi elde etmenizi sağlayan ertelenmiş eylemler ( anahtar kelime kullanarak )guard
anahtar kelime kullanarak ) normal hata denetimi / sinyal kodundan daha az / if kodu yazmanıza izin verir.Çalışma zamanı hataları:
Leandros'un çalışma zamanı hatalarını (ağ bağlantısı sorunları, veri ayrıştırma, dosya açma vb. Gibi) NSError
ele almasını önerdiği gibi, Foundation, AppKit, UIKit vb. Hatalarını bu şekilde bildirdiği için ObjC'de kullandığınız gibi kullanmalısınız . Yani dil dilinden çok çerçeve şey.
Kullanılan diğer bir sık desen AFNetworking gibi ayırıcı başarı / başarısızlık bloklarıdır:
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})
Yine de hata bloğu, sık sık alınan NSError
ve hatayı açıklayan örneği.
Programcı hataları:
Programcı hataları için (dizi elemanının sınır dışı erişimi, bir işlev çağrısına geçirilen geçersiz argümanlar vb.) ObjC'de istisnalar kullandınız. Swift dil istisnalar için herhangi bir dil desteğine sahip görünmüyor (gibi throw
, catch
vb anahtar kelime). Bununla birlikte, belgelerin önerdiği gibi ObjC ile aynı çalışma zamanında çalışmaktadır ve bu nedenle hala şu şekilde fırlatabilirsiniz NSExceptions
:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
ObjC kodunda istisnaları yakalamayı tercih etmenize rağmen, bunları sadece Swift'te yakalayamazsınız.
Soru, programcı hataları için istisnalar atmanız veya Apple'ın dil kılavuzunda önerdiği gibi iddiaları kullanmanız gerekip gerekmediğidir.
fatalError(...)
oyuk ile aynıdır.
9 Haziran 2015 Güncellemesi - Çok önemli
Swift 2.0 ile geliyor try
, throw
ve catch
anahtar kelimeler ve en heyecan verici olduğunu:
Swift, hata üreten Objective-C yöntemlerini Swift'in yerel hata işleme işlevine göre hata atan yöntemlere otomatik olarak çevirir.
Not: Temsilci yöntemler veya bir NSError nesne bağımsız değişkeniyle tamamlama işleyicisi alan yöntemler gibi hataları tüketen yöntemler, Swift tarafından içe aktarıldığında atanan yöntemler haline gelmez.
Alıntı: Apple Inc. “Swift'i Kakao ve Objective-C (Swift 2 Ön Yayın) ile kullanma.” iBooks.
Örnek: (kitaptan)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
NSLog(@"Error: %@", error.domain);
}
Hızlı eşdeğeri:
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
print ("Error: \(error.domain)")
}
Hata Atma:
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
Arayana otomatik olarak yayılacak:
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
Apple kitaplarından, Swift Programlama Dili, hataların enum kullanılarak ele alınması gerektiği anlaşılıyor.
İşte kitaptan bir örnek.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
Gönderen: Apple Inc. “Hızlı Programlama Dili.” iBooks. https://itun.es/br/jEUH0.l
Güncelleme
Apple haber kitaplarından "Swift'i Kakao ve Objective-C ile Kullanma". Hızlı diller kullanılarak çalışma zamanı istisnaları oluşmaz, bu yüzden deneme yakalamanız yoktur. Bunun yerine İsteğe Bağlı Zincirleme kullanırsınız .
İşte kitaptan bir streç:
Örneğin, aşağıdaki kod listesinde, NSDate nesnesinde length özelliği ve characterAtIndex: yöntemi bulunmadığından, birinci ve ikinci satırlar yürütülmez. MyLength sabiti isteğe bağlı bir Int olarak çıkarılır ve nil olarak ayarlanır. Üçüncü satırda gösterildiği gibi, nesnenin yanıt vermeyebileceği bir yöntemin sonucunu koşullu olarak açmak için bir if – let ifadesi de kullanabilirsiniz.
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
Alıntı: Apple Inc. “Swift'i Kakao ve Objektif-C ile Kullanma” iBooks. https://itun.es/br/1u3-0.l
Kitaplar ayrıca Objective-C'den (NSError Nesnesi) gelen kakao hata modelini kullanmanızı teşvik ediyor
Swift'teki hata bildirimi, Objective-C ile aynı modeli izler ve ayrıca isteğe bağlı dönüş değerleri sunmanın avantajını da içerir. En basit durumda, başarılı olup olmadığını belirtmek için işlevden bir Bool değeri döndürürsünüz. Hatanın nedenini bildirmeniz gerektiğinde, işleve NSErrorPointer türünde bir NSError out parametresi ekleyebilirsiniz. Bu tür, ek bellek güvenliği ve isteğe bağlı yazımla birlikte Objective-C'nin NSError ** ürününe kabaca eşdeğerdir. Aşağıdaki kod listesinde gösterildiği gibi, NSErrorPointer nesnesi olarak isteğe bağlı bir NSError türüne başvuru iletmek için önek ve işlecini kullanabilirsiniz.
var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
encoding: NSUTF8StringEncoding,
error: &writeError)
if !written {
if let error = writeError {
println("write failure: \(error.localizedDescription)")
}
}
Alıntı: Apple Inc. “Swift'i Kakao ve Objektif-C ile Kullanma” iBooks. https://itun.es/br/1u3-0.l
Swift'te Objective-C'nin yaklaşımına benzer bir İstisna yoktur.
Geliştirme sırasında, assert
üretime gitmeden önce ortaya çıkabilecek ve düzeltilmesi gereken hataları yakalamak için kullanabilirsiniz .
Klasik NSError
yaklaşım değişmez, bir NSErrorPointer
nüfus gönderirsiniz .
Kısa örnek:
var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
println("An error occurred \(error)")
} else {
println("Contents: \(contents)")
}
f();g();
olur f(&err);if(err) return;g(&err);if(err) return;
, o zaman olurf(nil);g(nil);hopeToGetHereAlive();
Önerilen 'Swift Way':
func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}
var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
println("write failure 1: \(writeError!.localizedDescription)")
// assert(false) // Terminate program
}
Ancak takip etmeyi daha kolay bulduğum için denemeyi / yakalamayı tercih ederim çünkü hata işlemeyi sonunda ayrı bir bloğa taşır, bu düzenlemeye bazen "Altın Yol" denir. Şanslı bunu kapanışlarla yapabilirsiniz:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
Ayrıca bir yeniden deneme tesisi eklemek kolaydır:
TryBool {
write("~/Error3")(error: $0) // The code to try
}.retry {
println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
return write("~/Error3r") // The code to retry
}.catch {
println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
TryBool için liste:
class TryBool {
typealias Tryee = NSErrorPointer -> Bool
typealias Catchee = NSError? -> ()
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return self.retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) {
var error: NSError?
for numRetries in 0...retries { // First try is retry 0
error = nil
let result = tryee(&error)
if result {
return
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
catchee(error)
}
}
Bool değeri yerine İsteğe bağlı bir döndürülen değeri test etmek için benzer bir sınıf yazabilirsiniz:
class TryOptional<T> {
typealias Tryee = NSErrorPointer -> T?
typealias Catchee = NSError? -> T
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) -> T {
var error: NSError?
for numRetries in 0...retries {
error = nil
let result = tryee(&error)
if let r = result {
return r
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
return catchee(error)
}
}
TryOptional sürümü, sonraki programlamayı kolaylaştıran İsteğe Bağlı olmayan bir dönüş türünü zorunlu kılar, örneğin 'Hızlı Yol:
struct FailableInitializer {
init?(_ id: Int, error: NSErrorPointer) {
// Always fails in example
if error != nil {
error.memory = NSError(domain: "", code: id, userInfo: [:])
}
return nil
}
private init() {
// Empty in example
}
static let fallback = FailableInitializer()
}
func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
return FailableInitializer(id, error: error)
}
var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
println("failableInitializer failure code: \(failError!.code)")
failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap
TryOptional'ı kullanma:
let failure2 = TryOptional {
failableInitializer(2)(error: $0)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
let failure3 = TryOptional {
failableInitializer(3)(error: $0)
}.retry {
println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
return failableInitializer(31)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
Otomatik açmayı not edin.
Düzenle: Bu cevap işe yarıyor olsa da, Swift-dönüştürülmüş Objective-C biraz daha fazla. Swift 2.0'daki değişikliklerle geçersiz hale getirildi. Guilherme Torres Castro'nun yukarıdaki cevabı, Swift'teki hataları ele almanın tercih edilen yoluna çok iyi bir giriş. Vos
Anlamak biraz zaman aldı ama sanırım bunu batırdım. Gerçi çirkin gözüküyor. Objective-C versiyonu üzerinde ince bir ciltten başka bir şey yoktur.
NSError parametresiyle bir işlev çağrılıyor ...
var fooError : NSError ? = nil
let someObject = foo(aParam, error:&fooError)
// Check something was returned and look for an error if it wasn't.
if !someObject {
if let error = fooError {
// Handle error
NSLog("This happened: \(error.localizedDescription)")
}
} else {
// Handle success
}`
Bir hata parametresi alan işlev yazılıyor ...
func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {
// Do stuff...
if somethingBadHasHappened {
if error {
error.memory = NSError(domain: domain, code: code, userInfo: [:])
}
return nil
}
// Do more stuff...
}
Deneme yakalama özelliğini veren objektif C etrafındaki temel sarıcı. https://github.com/williamFalcon/SwiftTryCatch
Gibi kullanın:
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
Bu, swift 2.0 için bir güncelleme cevabıdır. Java gibi özellik zengin Hata işleme modeli bekliyorum. Sonunda iyi haberi duyurdular. buraya
Hata işleme modeli: Swift 2.0'daki yeni hata işleme modeli, tanıdık deneme, atma ve yakalama anahtar kelimeleriyle anında doğal bir hal alacaktır . Hepsinden iyisi, Apple SDK'ları ve NSError ile mükemmel çalışacak şekilde tasarlanmıştır. Aslında, NSError bir Swift'in ErrorType'ına uygundur. Bu konuda daha fazla şey duymak için Swift'teki Yenilikler hakkındaki WWDC oturumunu kesinlikle izlemek isteyeceksiniz.
Örneğin :
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}
Guilherme Torres Castro söylediği gibi, Swift 2.0, try
, catch
,do
programlamada kullanılabilir.
Örneğin, CoreData getirme veri yönteminde, &error
içine bir parametre olarak koymak yerine managedContext.executeFetchRequest(fetchRequest, error: &error)
, şimdi sadece kullanımı kullanmalı managedContext.executeFetchRequest(fetchRequest)
ve ardından try
, catch
( Apple Document Link )
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults{
people = results
}
} catch {
print("Could not fetch")
}
Xcode7 Beta sürümünü zaten indirdiyseniz. Aramayı deneyin hatalarına yol açan yılında Dokümantasyon ve API Başvurusu ve birinci gösteren sonucunu seçmek, bu yeni sözdizimi için neler yapılabileceğini temel bir fikir verir. Ancak, henüz birçok API için tam dokümantasyon gönderilmemiştir.
Daha süslü Hata İşleme teknikleri şurada bulunabilir:
What's New in Swift (2015 Oturumu 106 28m30s)
Hata işleme, Swift 2.0'ın yeni bir özelliğidir. Bu kullanır try
, throw
ve catch
anahtar kelimeler.
İstisnalarla başa çıkmak için hoş ve basit bir lib: TryCatchFinally-Swift
Diğer birkaç kişi gibi, nesnel C istisna özelliklerinin etrafını sarar.
Şöyle kullanın:
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
Swift 2'den başlayarak, diğerlerinin de belirttiği gibi, hata işleme en iyi do / try / catch ve ErrorType enum'ları kullanılarak gerçekleştirilir. Bu, senkronize yöntemler için oldukça iyi çalışır, ancak eşzamansız hata işleme için biraz akıllılık gerekir.
Bu makalede bu soruna büyük bir yaklaşım vardır:
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
Özetlemek:
// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData
// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
{
completionHandler()
}
daha sonra, yukarıdaki yönteme çağrı aşağıdaki gibi olacaktır:
self.loadData("someString",
completionHandler:
{ result: LoadDataResult in
do
{
let data = try result()
// success - go ahead and work with the data
}
catch
{
// failure - look at the error code and handle accordingly
}
})
Bu, ayrı bir hataya sahip olmaktan biraz daha temiz görünüyor.Andler geri çağrışım, eşzamansız işleve iletildi, bu da Swift 2'den önce ele alınacaktı.
Gördüğüm, cihazın doğası gereği, kullanıcıya bir grup şifreli hata işleme mesajı atmak istemediğinizdir. Bu yüzden çoğu işlev isteğe bağlı değerleri döndürür, sonra isteğe bağlı olarak yoksaymak için kodlarsınız. Eğer bir işlev başarısız olduğu anlamına gelmezse bir mesaj ya da her neyse başlatabilirsiniz.