Swift 2: Panggilan dapat dilemparkan, tetapi tidak ditandai dengan 'coba' dan kesalahan tidak ditangani


161

Setelah saya menginstal Xcode 7 beta dan mengkonversi kode swift saya ke Swift 2, saya mendapatkan beberapa masalah dengan kode yang tidak dapat saya pahami. Saya tahu Swift 2 baru, jadi saya mencari dan mencari tahu karena tidak ada apa-apa tentang itu, saya harus menulis pertanyaan.

Inilah kesalahannya:

Panggilan dapat dilemparkan, tetapi tidak ditandai dengan 'coba' dan kesalahannya tidak ditangani

Kode:

func deleteAccountDetail(){
        let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        //The Line Below is where i expect the error
        let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
        }

        do {
            try self.Context!.save()
        } catch _ {
        }

    }

Foto: masukkan deskripsi gambar di sini

Jawaban:


168

Anda harus menangkap kesalahan sama seperti yang sudah Anda lakukan untuk save()panggilan Anda dan karena Anda menangani beberapa kesalahan di sini, Anda dapat melakukan trybanyak panggilan secara berurutan dalam satu blok tangkapan, seperti:

func deleteAccountDetail() {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    do {
        let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
            self.Context!.deleteObject(entity)
        }

        try self.Context!.save()
    } catch {
        print(error)
    }
}

Atau seperti yang ditunjukkan @ bames53 dalam komentar di bawah, sering kali lebih baik tidak menangkap kesalahan ketika dilemparkan. Anda dapat menandai metode saat throwsitu tryuntuk memanggil metode. Sebagai contoh:

func deleteAccountDetail() throws {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()

    request.entity = entityDescription

    let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]

    for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
    }

    try self.Context!.save()
}

Ini membantu saya untuk mencari tahu, Terima kasih.
Farhad

5
Sebenarnya tidak perlu bahwa pengecualian ditangkap di sini. Mungkin saja menambahkan trykata kunci ke panggilan fungsi, dan menyatakan fungsi ini sebagai func deleteAccountDetail() throw. Atau jika Anda telah menjamin bahwa fungsi tersebut tidak akan membuang input yang diberikan, Anda dapat menggunakannya try!.
bames53

4
Saya membawa ini bukan untuk nitpick, tetapi karena sebenarnya cukup penting untuk penanganan kesalahan berbasis pengecualian yang layak bahwa sebagian besar tempat di mana pengecualian terjadi tidak menangkap pengecualian. Ada tiga jenis tempat di mana menangkap pengecualian sesuai. Di semua tempat lain, kode tidak boleh menangani pengecualian secara eksplisit, dan harus bergantung pada deinit()panggilan implisit untuk melakukan pembersihan (yaitu, RAII), atau sesekali digunakan deferuntuk melakukan pembersihan ad hoc. Lihat exceptionionsafecode.com untuk informasi lebih lanjut (Ini berbicara tentang C ++, tetapi prinsip-prinsip dasar berlaku untuk pengecualian Swift juga.)
bames53

Tetapi bagaimana Anda menjalankan fungsinya? Jika saya menggunakan cara @ bames53?
Farhad

1
@NickMoore Apa yang dipilih pengembang Swift untuk memanggil mereka tidak membuat perbedaan dalam apa yang sebenarnya. Sistem penanganan kesalahan baru Swift adalah implementasi pengecualian karena istilah itu biasa digunakan di seluruh industri.
bames53

41

Saat memanggil fungsi yang dideklarasikan dengan throwsdi Swift, Anda harus membuat anotasi situs panggilan fungsi dengan tryatau try!. Misalnya, diberi fungsi melempar:

func willOnlyThrowIfTrue(value: Bool) throws {
  if value { throw someError }
}

fungsi ini bisa disebut seperti:

func foo(value: Bool) throws {
  try willOnlyThrowIfTrue(value)
}

Di sini kita membubuhi keterangan panggilan try, yang memanggil pembaca bahwa fungsi ini dapat menimbulkan pengecualian, dan setiap baris kode berikut mungkin tidak dieksekusi. Kita juga harus membubuhi keterangan fungsi ini throws, karena fungsi ini bisa melempar pengecualian (yaitu, ketika willOnlyThrowIfTrue()melempar, maka fooakan secara otomatis menggabungkan kembali pengecualian ke atas.

Jika Anda ingin memanggil fungsi yang dinyatakan sebagai kemungkinan melempar, tetapi yang Anda tahu tidak akan melempar dalam kasus Anda karena Anda memberikan input yang benar, Anda dapat menggunakannya try!.

func bar() {
  try! willOnlyThrowIfTrue(false)
}

Dengan cara ini, ketika Anda menjamin bahwa kode tidak akan dibuang, Anda tidak perlu memasukkan kode boilerplate tambahan untuk menonaktifkan propagasi pengecualian.

try!diberlakukan pada saat runtime: jika Anda menggunakan try!dan fungsi tersebut akhirnya membuang, maka eksekusi program Anda akan diakhiri dengan kesalahan runtime.

Sebagian besar pengecualian menangani kode harus terlihat seperti di atas: Anda cukup menyebarkan pengecualian ke atas ketika mereka terjadi, atau Anda mengatur kondisi sehingga pengecualian yang mungkin dikesampingkan. Setiap pembersihan sumber daya lain dalam kode Anda harus terjadi melalui penghancuran objek (yaitu deinit()), atau kadang-kadang melalui deferkode ed.

func baz(value: Bool) throws {

  var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
  var data = NSData(contentsOfFile:filePath)

  try willOnlyThrowIfTrue(value)

  // data and filePath automatically cleaned up, even when an exception occurs.
}

Jika karena alasan apa pun Anda memiliki kode pembersihan yang perlu dijalankan tetapi tidak deinit()berfungsi, Anda dapat menggunakannya defer.

func qux(value: Bool) throws {
  defer {
    print("this code runs when the function exits, even when it exits by an exception")
  }

  try willOnlyThrowIfTrue(value)
}

Sebagian besar kode yang berhubungan dengan pengecualian hanya membuat mereka menyebar ke atas ke penelepon, melakukan pembersihan di jalan melalui deinit()atau defer. Ini karena sebagian besar kode tidak tahu apa yang harus dilakukan dengan kesalahan; ia tahu apa yang salah, tetapi tidak memiliki informasi yang cukup tentang apa yang beberapa kode tingkat tinggi coba lakukan untuk mengetahui apa yang harus dilakukan tentang kesalahan. Itu tidak tahu apakah menyajikan dialog kepada pengguna sesuai, atau apakah itu harus mencoba lagi, atau jika sesuatu yang lain sesuai.

Namun, kode tingkat yang lebih tinggi harus tahu persis apa yang harus dilakukan jika terjadi kesalahan. Jadi pengecualian memungkinkan kesalahan spesifik muncul dari tempat awalnya terjadi ke tempat penanganannya.

Menangani pengecualian dilakukan melalui catchpernyataan.

func quux(value: Bool) {
  do {
    try willOnlyThrowIfTrue(value)
  } catch {
    // handle error
  }
}

Anda dapat memiliki beberapa pernyataan penangkapan, masing-masing menangkap jenis pengecualian yang berbeda.

  do {
    try someFunctionThatThowsDifferentExceptions()
  } catch MyErrorType.errorA {
    // handle errorA
  } catch MyErrorType.errorB {
    // handle errorB
  } catch {
    // handle other errors
  }

Untuk detail lebih lanjut tentang praktik terbaik dengan pengecualian, lihat http://exceptionsafecode.com/ . Ini khusus ditujukan untuk C ++, tetapi setelah memeriksa model pengecualian Swift, saya percaya dasar-dasar berlaku untuk Swift juga.

Untuk detail tentang sintaks Swift dan model penanganan kesalahan, lihat buku Swift Programming Language (Swift 2 Prerelease) .


Pada dasarnya menangkap itu sendiri dapat menangani kesalahan? atau fungsi input
Farhad

1
@BrianS Saya tidak yakin persis apa yang Anda minta, terutama yang berkaitan dengan 'fungsi input', tetapi 'menangkap' pada dasarnya adalah sinonim untuk 'pegangan' dalam konteks pengecualian. Artinya, menangkap pengecualian dan menangani pengecualian adalah hal yang sama, sejauh menyangkut bahasa pemrograman.
bames53

Saya punya satu kesalahan, saya diam tidak mengerti, Bisakah Anda membantu saya? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad

@BrianS Sepertinya Anda menggunakan fungsi dengan tanda tangan yang salah di suatu tempat. Sesuatu mengharapkan diberikan fungsi yang mengambil NSData?, NSURLResponse?, NSError?sebagai argumen, tetapi Anda memberinya fungsi yang tidak mengambil argumen apa pun.
bames53

Atau sesuatu mengharapkan fungsi yang tidak dinyatakan untuk melempar pengecualian dan Anda memberinya fungsi yang melempar pengecualian.
bames53
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.