Saya belum banyak membaca Swift tetapi satu hal yang saya perhatikan adalah tidak ada pengecualian. Jadi bagaimana mereka melakukan penanganan kesalahan di Swift? Adakah yang menemukan sesuatu yang berhubungan dengan penanganan kesalahan?
Saya belum banyak membaca Swift tetapi satu hal yang saya perhatikan adalah tidak ada pengecualian. Jadi bagaimana mereka melakukan penanganan kesalahan di Swift? Adakah yang menemukan sesuatu yang berhubungan dengan penanganan kesalahan?
Jawaban:
Banyak hal telah berubah sedikit di Swift 2, karena ada mekanisme penanganan kesalahan baru, yang agak lebih mirip dengan pengecualian tetapi berbeda dalam detailnya.
Jika fungsi / metode ingin menunjukkan bahwa ia dapat menimbulkan kesalahan, itu harus mengandung throws
kata kunci seperti ini
func summonDefaultDragon() throws -> Dragon
Catatan: tidak ada spesifikasi untuk jenis kesalahan yang dapat dilemparkan oleh fungsi tersebut. Deklarasi ini hanya menyatakan bahwa fungsi tersebut dapat melempar instance dari jenis apa pun yang menerapkan ErrorType atau tidak melempar sama sekali.
Untuk menjalankan fungsi, Anda perlu menggunakan kata kunci coba, seperti ini
try summonDefaultDragon()
baris ini biasanya harus ada blok do-catch seperti ini
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
}
Catatan: catch clause menggunakan semua fitur canggih pencocokan pola Swift sehingga Anda sangat fleksibel di sini.
Anda dapat memutuskan untuk menyebarkan kesalahan, jika Anda memanggil fungsi melempar dari fungsi yang ditandai dengan throws
kata kunci:
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
Atau, Anda dapat memanggil fungsi melempar menggunakan try?
:
let dragonOrNil = try? summonDefaultDragon()
Dengan cara ini Anda mendapatkan nilai kembali atau nihil, jika terjadi kesalahan. Dengan menggunakan cara ini Anda tidak mendapatkan objek kesalahan.
Yang berarti Anda juga dapat menggabungkan try?
dengan pernyataan berguna seperti:
if let dragon = try? summonDefaultDragon()
atau
guard let dragon = try? summonDefaultDragon() else { ... }
Akhirnya, Anda dapat memutuskan bahwa Anda tahu bahwa kesalahan tidak akan benar-benar terjadi (misalnya karena Anda telah memeriksa prasyarat) dan menggunakan try!
kata kunci:
let dragon = try! summonDefaultDragon()
Jika fungsi tersebut benar-benar melempar kesalahan, maka Anda akan mendapatkan kesalahan runtime di aplikasi Anda dan aplikasi akan berakhir.
Untuk melempar kesalahan, Anda menggunakan kata kunci melempar seperti ini
throw DragonError.dragonIsMissing
Anda dapat membuang apa pun yang sesuai dengan ErrorType
protokol. Sebagai permulaan NSError
sesuai dengan protokol ini, tetapi Anda mungkin ingin menggunakan berbasis enum ErrorType
yang memungkinkan Anda untuk mengelompokkan beberapa kesalahan terkait, berpotensi dengan potongan data tambahan, seperti ini
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
Perbedaan utama antara mekanisme kesalahan Swift 2 & 3 baru dan pengecualian gaya Java / C # / C ++ adalah sebagai berikut:
do-catch
+ try
+ defer
vs try-catch-finally
sintaksis tradisional .do-catch
Blok Anda tidak akan menangkap NSException, dan sebaliknya, untuk itu Anda harus menggunakan ObjC.NSError
konvensi metode Kakao untuk mengembalikan false
(untuk Bool
fungsi kembali) atau nil
(untuk AnyObject
fungsi kembali) dan meneruskan NSErrorPointer
dengan detail kesalahan.Sebagai tambahan sintaksis-gula untuk memudahkan penanganan kesalahan, ada dua konsep lagi
defer
kata kunci) yang memungkinkan Anda mencapai efek yang sama dengan akhirnya memblokir di Java / C # / etcguard
kata kunci) yang memungkinkan Anda menulis kode if / else lebih sedikit dari pada kode pengecekan / pensinyalan kesalahan normal.Kesalahan runtime:
Seperti yang disarankan Leandros untuk menangani kesalahan runtime (seperti masalah konektivitas jaringan, parsing data, file pembukaan, dll) Anda harus menggunakan NSError
seperti yang Anda lakukan di ObjC, karena Foundation, AppKit, UIKit, dll melaporkan kesalahan mereka dengan cara ini. Jadi lebih banyak hal kerangka daripada hal bahasa.
Pola lain yang sering digunakan adalah blok sukses / gagal pemisah seperti di AFNetworking:
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")
})
Masih blok kegagalan sering diterima NSError
contoh, menggambarkan kesalahan.
Kesalahan pemrogram:
Untuk kesalahan programmer (seperti akses di luar batas elemen array, argumen tidak valid diteruskan ke panggilan fungsi, dll) Anda menggunakan pengecualian di ObjC. Bahasa Swift tampaknya tidak memiliki dukungan bahasa untuk pengecualian (seperti throw
, catch
, dll kata kunci). Namun, seperti yang ditunjukkan oleh dokumentasi, ia menjalankan runtime yang sama dengan ObjC, dan karenanya Anda masih dapat melempar NSExceptions
seperti ini:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
Anda tidak dapat menangkapnya dalam Swift murni, meskipun Anda dapat memilih untuk menangkap pengecualian dalam kode ObjC.
Pertanyaannya adalah apakah Anda harus melempar pengecualian untuk kesalahan programmer, atau lebih tepatnya menggunakan pernyataan seperti yang disarankan Apple dalam panduan bahasa.
fatalError(...)
sama juga.
Perbarui 9 Juni 2015 - Sangat penting
Swift 2.0 hadir dengan try
,, throw
dan catch
kata kunci dan yang paling menarik adalah:
Swift secara otomatis menerjemahkan metode Objective-C yang menghasilkan kesalahan ke dalam metode yang melempar kesalahan sesuai dengan fungsionalitas penanganan kesalahan asli Swift.
Catatan: Metode yang menggunakan kesalahan, seperti metode mendelegasikan atau metode yang mengambil penangan penyelesaian dengan argumen objek NSError, jangan menjadi metode yang melempar ketika diimpor oleh Swift.
Kutipan dari: Apple Inc. “Menggunakan Swift dengan Kakao dan Objective-C (Swift 2 Prerelease).” iBooks.
Contoh: (dari buku)
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);
}
Setara dengan swift adalah:
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)")
}
Melempar Kesalahan:
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
Akan secara otomatis disebarkan ke penelepon:
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
Dari buku-buku Apple, The Swift Programming Language, tampaknya kesalahan harus ditangani menggunakan enum.
Ini adalah contoh dari buku ini.
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)"
}
Dari: Apple Inc. "Bahasa Pemrograman Swift." iBooks. https://itun.es/br/jEUH0.l
Memperbarui
Dari buku-buku berita Apple, "Menggunakan Swift dengan Kakao dan Objective-C". Pengecualian runtime tidak terjadi menggunakan bahasa cepat, jadi itu sebabnya Anda tidak memiliki try-catch. Sebagai gantinya Anda menggunakan Chaining Opsional .
Ini adalah peregangan dari buku ini:
Misalnya, dalam daftar kode di bawah ini, baris pertama dan kedua tidak dieksekusi karena properti panjang dan metode characterAtIndex: tidak ada pada objek NSDate. Konstanta myLength disimpulkan sebagai Int opsional, dan diatur ke nol. Anda juga dapat menggunakan pernyataan if-let untuk membukanya secara kondisional dari suatu metode yang objeknya mungkin tidak merespons, seperti yang ditunjukkan pada baris tiga
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
Kutipan dari: Apple Inc. “Menggunakan Swift dengan Kakao dan Objective-C.” iBooks. https://itun.es/br/1u3-0.l
Dan buku-buku itu juga mendorong Anda untuk menggunakan pola kesalahan kakao dari Objective-C (NSError Object)
Pelaporan kesalahan di Swift mengikuti pola yang sama dengan yang dilakukan di Objective-C, dengan manfaat tambahan dari menawarkan nilai pengembalian opsional. Dalam kasus paling sederhana, Anda mengembalikan nilai Bool dari fungsi untuk menunjukkan apakah itu berhasil atau tidak. Saat Anda perlu melaporkan alasan kesalahan, Anda dapat menambahkan ke fungsi parameter NSError out dari tipe NSErrorPointer. Jenis ini kira-kira setara dengan NSError Objective-C **, dengan keamanan memori tambahan dan pengetikan opsional. Anda dapat menggunakan awalan & operator untuk meneruskan referensi ke jenis NSError opsional sebagai objek NSErrorPointer, seperti yang ditunjukkan dalam daftar kode di bawah ini.
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)")
}
}
Kutipan dari: Apple Inc. “Menggunakan Swift dengan Kakao dan Objective-C.” iBooks. https://itun.es/br/1u3-0.l
Tidak ada Pengecualian di Swift, mirip dengan pendekatan Objective-C.
Dalam pengembangan, Anda dapat menggunakan assert
untuk menangkap kesalahan yang mungkin muncul, dan perlu diperbaiki sebelum mulai berproduksi.
NSError
Pendekatan klasik tidak diubah, Anda mengirim NSErrorPointer
, yang akan diisi.
Contoh singkat:
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();
menjadi f(&err);if(err) return;g(&err);if(err) return;
untuk bulan pertama, maka itu hanya menjadif(nil);g(nil);hopeToGetHereAlive();
'Swift Way' yang disarankan adalah:
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
}
Namun saya lebih suka mencoba / menangkap karena saya merasa lebih mudah untuk mengikuti karena memindahkan penanganan kesalahan ke blok yang terpisah di akhir, pengaturan ini kadang-kadang disebut "Jalur Emas". Beruntung Anda bisa melakukan ini dengan penutupan:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
Juga mudah untuk menambahkan fasilitas coba lagi:
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
}
Daftar untuk TryBool adalah:
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)
}
}
Anda dapat menulis kelas serupa untuk menguji nilai yang dikembalikan opsional dan bukan nilai Bool:
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)
}
}
Versi TryOptional memberlakukan jenis pengembalian non-opsional yang membuat pemrograman berikutnya lebih mudah, misalnya 'Swift Way:
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
Menggunakan TryOptional:
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
}
Catatan buka otomatis.
Edit: Walaupun jawaban ini berhasil, ini lebih dari Objective-C yang ditransliterasikan ke dalam Swift. Itu telah dibuat usang oleh perubahan dalam Swift 2.0. Jawaban Guilherme Torres Castro di atas adalah pengantar yang sangat bagus untuk cara penanganan kesalahan yang disukai di Swift. VOS
Butuh sedikit mencari tahu tapi saya pikir saya sudah sussed itu. Sepertinya jelek. Tidak lebih dari kulit tipis di atas versi Objective-C.
Memanggil fungsi dengan parameter NSError ...
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
}`
Menulis fungsi yang mengambil parameter kesalahan ...
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...
}
Pembungkus dasar di sekitar tujuan C yang memberi Anda fitur coba tangkap. https://github.com/williamFalcon/SwiftTryCatch
Gunakan seperti:
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
Ini adalah jawaban pembaruan untuk swift 2.0. Saya menantikan fitur penanganan model yang kaya fitur seperti di java. Akhirnya, mereka mengumumkan kabar baik. sini
Model penanganan kesalahan: Model penanganan kesalahan baru di Swift 2.0 akan langsung terasa alami, dengan mencoba, melempar, dan menangkap kata kunci yang familier . Terbaik dari semua, itu dirancang untuk bekerja dengan sempurna dengan SDK Apple dan NSError. Bahkan, NSError sesuai dengan ErrorType Swift. Anda pasti ingin menonton sesi WWDC di What's New in Swift untuk mendengar lebih banyak tentangnya.
misalnya:
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}
Sebagai Guilherme Torres Castro mengatakan, di Swift 2.0, try
, catch
,do
dapat digunakan dalam pemrograman.
Sebagai contoh, Di CoreData mengambil metode data, bukannya dimasukkan &error
sebagai parameter ke dalammanagedContext.executeFetchRequest(fetchRequest, error: &error)
, sekarang kita hanya perlu menggunakan gunakan managedContext.executeFetchRequest(fetchRequest)
dan kemudian menangani kesalahan dengan 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")
}
Jika Anda sudah mengunduh xcode7 Beta. Cobalah untuk mencari kesalahan melempar dalam Dokumentasi dan Referensi API dan pilih hasil pertama yang ditampilkan, itu memberikan ide dasar apa yang dapat dilakukan untuk sintaks baru ini. Namun, dokumentasi lengkap belum dikirim untuk banyak API.
Teknik penanganan kesalahan yang lebih mewah dapat ditemukan di
What's New in Swift (Sesi 2015 106 28m30s)
Penanganan kesalahan adalah fitur baru dari Swift 2.0. Ini menggunakan try
, throw
dancatch
kata kunci.
Lib bagus dan sederhana untuk menangani pengecualian: TryCatchFinally-Swift
Seperti beberapa yang lain, ia membungkus fitur pengecualian C objektif.
Gunakan seperti ini:
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
Dimulai dengan Swift 2, seperti yang telah disebutkan orang lain, penanganan kesalahan paling baik dilakukan melalui penggunaan do / try / catch dan enum ErrorType. Ini bekerja cukup baik untuk metode sinkron, tetapi sedikit kepintaran diperlukan untuk penanganan kesalahan yang tidak sinkron.
Artikel ini memiliki pendekatan yang bagus untuk masalah ini:
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
Untuk meringkas:
// 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()
}
kemudian, panggilan ke metode di atas adalah sebagai berikut:
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
}
})
Ini tampaknya sedikit lebih bersih daripada memiliki error yang terpisah Callback handler dilewatkan ke fungsi asinkron, yang bagaimana ini akan ditangani sebelum Swift 2.
Apa yang saya lihat adalah bahwa karena sifat perangkat Anda tidak ingin melempar sekelompok pesan penanganan kesalahan cryptic pada pengguna. Itu sebabnya sebagian besar fungsi mengembalikan nilai opsional maka Anda hanya kode untuk mengabaikan opsional. Jika suatu fungsi kembali nihil artinya gagal Anda dapat pop pesan atau apa pun.