melempar pengecualian dalam objektif-c / kakao

Jawaban:

528

Saya menggunakan [NSException raise:format:]sebagai berikut:

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
e.James
sumber
9
Saya lebih suka cara ini sebagai @throw([NSException exceptionWith…])pendekatan pendekatan karena lebih ringkas.
Sam Soffes
9
Pastikan untuk membaca peringatan penting dari bahaya ( stackoverflow.com/questions/324284/324805#324805 )
e.James
26
Saya biasanya lebih suka ini, tetapi ada satu gotcha. Mungkin hanya versi Xcode saya saat ini, tetapi sintaks [NSException kenaikan ...] tampaknya tidak dikenali oleh parser sebagai jalur keluar dari metode yang mengembalikan nilai. Saya melihat peringatan "Kontrol dapat mencapai akhir fungsi non-void" saat menggunakan sintaks ini, tetapi dengan sintaks @throw ([NSException exceptionWith…]), parser mengenali itu sebagai jalan keluar dan tidak menampilkan peringatan.
mattorb
1
@mpstx Saya selalu menggunakan sintaks lempar untuk alasan yang Anda berikan (yang masih relevan dua tahun kemudian dalam Xcode 4.6, dan mungkin selalu akan). Memiliki IDE mengenali bahwa melempar pengecualian adalah fungsi titik keluar sering penting jika Anda ingin menghindari peringatan.
Mark Amery
FWIW Saya memperhatikan bahwa @ try / @ catch blocks juga menghasilkan false-negative untuk peringatan "control reach end of non-void function" (artinya peringatan tidak ditampilkan sebagaimana mestinya)
Brian Gerstle
256

Kata hati-hati di sini. Di Objective-C, tidak seperti banyak bahasa serupa, Anda umumnya harus mencoba untuk menghindari menggunakan pengecualian untuk situasi kesalahan umum yang mungkin terjadi dalam operasi normal.

Dokumentasi Apple untuk Obj-C 2.0 menyatakan sebagai berikut: "Penting: Pengecualian adalah sumber daya intensif di Objective-C. Anda tidak boleh menggunakan pengecualian untuk kontrol aliran umum, atau hanya untuk menandakan kesalahan (seperti file tidak dapat diakses)"

Dokumentasi penanganan pengecualian konseptual Apple menjelaskan hal yang sama, tetapi dengan lebih banyak kata-kata: "Penting: Anda harus mencadangkan penggunaan pengecualian untuk pemrograman atau kesalahan runtime yang tidak terduga seperti akses koleksi di luar batas, upaya untuk bermutasi objek yang tidak dapat diubah, mengirim pesan yang tidak valid, mengirim pesan yang tidak valid , dan kehilangan koneksi ke server jendela. Anda biasanya menangani kesalahan semacam ini dengan pengecualian saat aplikasi sedang dibuat daripada saat runtime. [.....] Alih-alih pengecualian, objek kesalahan (NSError) dan Mekanisme pengiriman kesalahan kakao adalah cara yang disarankan untuk mengkomunikasikan kesalahan yang diharapkan dalam aplikasi Kakao. "

Alasan untuk ini adalah sebagian untuk mematuhi idiom pemrograman di Objective-C (menggunakan nilai kembali dalam kasus-kasus sederhana dan parameter referensi tambahan (sering kelas NSError) dalam kasus yang lebih kompleks), sebagian yang melempar dan menangkap pengecualian jauh lebih mahal dan akhirnya (dan mengandaikan paling penting) bahwa pengecualian Objective-C adalah pembungkus tipis di sekitar fungsi setjmp () dan longjmp (), yang pada dasarnya mengacaukan penanganan memori Anda yang hati-hati, lihat penjelasan ini .

merugikan
sumber
11
Saya pikir ini berlaku untuk sebagian besar bahasa pemrograman: "cobalah untuk menghindari menggunakan pengecualian untuk situasi kesalahan umum". Hal yang sama berlaku di Jawa; praktik buruk untuk menangani kesalahan input pengguna (misalnya) dengan pengecualian. Bukan hanya karena penggunaan sumber daya, tetapi juga untuk kejelasan kode.
beetstra
6
Lebih penting lagi, Pengecualian dalam Kakao dirancang untuk menunjukkan kesalahan program yang tidak dapat dipulihkan. Melakukan hal yang sebaliknya bertentangan dengan kerangka kerja dan dapat menyebabkan perilaku yang tidak jelas. Lihat stackoverflow.com/questions/3378696/iphone-try-end-try/… untuk detailnya.
KPM
9
"Hal yang sama berlaku di Jawa;" Tidak setuju. Anda dapat menggunakan pengecualian yang diperiksa di Jawa dengan baik untuk kondisi kesalahan normal. Tentu saja Anda tidak akan menggunakan pengecualian Runtime.
Daniel Ryan
Saya lebih suka cara Kakao (pengecualian hanya untuk kesalahan programmer) jadi saya lebih suka melakukannya di Jawa juga, tetapi kenyataannya adalah Anda harus mengikuti praktik tipikal di lingkungan, dan pengecualian untuk penanganan kesalahan menonjol seperti bau busuk di Objective-C, tetapi banyak digunakan untuk tujuan itu di Jawa.
gnasher729
1
Komentar ini tidak menjawab pertanyaan. Mungkin OP hanya ingin menutup aplikasi untuk menguji apakah kerangka kerja laporan kerusakan berfungsi seperti yang diharapkan.
Simon
62
@throw([NSException exceptionWith…])

Xcode mengenali @throwpernyataan sebagai titik keluar fungsi, seperti returnpernyataan. Menggunakan @throwsintaks menghindari peringatan salah " Kontrol dapat mencapai akhir fungsi tidak-kosong " yang mungkin Anda peroleh [NSException raise:…].

Juga, @throwdapat digunakan untuk melempar objek yang bukan dari kelas NSException.

Peter Hosey
sumber
11
@Steph Thirion: Lihat developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/… untuk semua detailnya. Intinya? Keduanya akan berfungsi, tetapi @throw dapat digunakan untuk melempar objek yang bukan dari kelas NSException.
e.James
33

Mengenai [NSException raise:format:]. Bagi mereka yang berasal dari latar belakang Java, Anda akan ingat bahwa Java membedakan antara Exception dan RuntimeException. Pengecualian adalah pengecualian yang dicentang, dan RuntimeException tidak dicentang. Secara khusus, Java menyarankan untuk menggunakan pengecualian yang diperiksa untuk "kondisi kesalahan normal" dan pengecualian yang tidak dicentang untuk "kesalahan runtime yang disebabkan oleh kesalahan pemrogram." Tampaknya pengecualian Objective-C harus digunakan di tempat yang sama dengan pengecualian yang tidak dicentang, dan nilai pengembalian kode kesalahan atau nilai NSError lebih disukai di tempat di mana Anda akan menggunakan pengecualian yang dicentang.

Daniel Yankowsky
sumber
1
Ya ini benar (setelah 4 tahun: D), buat kelas kesalahan Anda sendiri ABCError yang memanjang dari kelas NSError dan gunakan untuk mengecek pengecualian daripada persepsi NSE. Naikkan persepsi NSE di mana kesalahan programmer (situasi tak terduga seperti masalah format angka) terjadi.
chathuram
15

Saya pikir untuk menjadi konsisten, lebih baik menggunakan @throw dengan kelas Anda sendiri yang memperluas NSException. Kemudian Anda menggunakan notasi yang sama untuk try catch akhirnya:

@try {
.....
}
@catch{
...
}
@finally{
...
}

Apple menjelaskan di sini cara melempar dan menangani pengecualian: Menangkap Pengecualian Melontar Pengecualian

rustyshelf
sumber
saya masih mendapatkan crash dengan pengecualian runtime di blok coba
famfamfam
14

Sejak ObjC 2.0, pengecualian Objective-C tidak lagi menjadi pembungkus untuk setjmp () longjmp (), dan kompatibel dengan pengecualian C ++, @try adalah "gratis", tetapi melempar dan menangkap pengecualian jauh lebih mahal.

Bagaimanapun, pernyataan (menggunakan NSAssert dan keluarga makro NSCAssert) melempar NSException, dan itu waras untuk menggunakannya sebagai negara Ries.

Psiko
sumber
Senang mendengarnya! Kami memiliki pustaka pihak ketiga yang tidak ingin kami modifikasi yang memberikan pengecualian untuk kesalahan terkecil sekalipun. Kita harus menangkap mereka di satu tempat di aplikasi dan itu hanya membuat kita merasa ngeri, tetapi ini membuat saya merasa sedikit lebih baik.
Yuri Brigance
8

Gunakan NSError untuk mengkomunikasikan kegagalan daripada pengecualian.

Poin cepat tentang NSError:

  • NSError memungkinkan kode kesalahan gaya C (bilangan bulat) dengan jelas mengidentifikasi akar penyebab dan mudah-mudahan memungkinkan penangan kesalahan untuk mengatasi kesalahan. Anda dapat membungkus kode kesalahan dari pustaka C seperti SQLite dalam instance NSError dengan sangat mudah.

  • NSError juga memiliki manfaat menjadi objek dan menawarkan cara untuk menggambarkan kesalahan secara lebih rinci dengan anggota kamus userInfo.

  • Tapi yang terbaik dari semuanya, NSError TIDAK BISA dilemparkan sehingga mendorong pendekatan yang lebih proaktif untuk penanganan kesalahan, berbeda dengan bahasa lain yang hanya membuang kentang lebih jauh dan lebih jauh ke tumpukan panggilan di mana saat itu hanya dapat dilaporkan kepada pengguna dan tidak ditangani dengan cara yang berarti (tidak jika Anda yakin mengikuti prinsip menyembunyikan informasi terbesar OOP).

Referensi Tautan: Referensi

Jason Fuerstenberg
sumber
Komentar ini tidak menjawab pertanyaan. Mungkin OP hanya ingin menutup aplikasi untuk menguji apakah kerangka kerja laporan kerusakan berfungsi seperti yang diharapkan.
Simon
7

Ini adalah bagaimana saya mempelajarinya dari "Panduan Peternakan Besar Nerd (edisi ke-4)":

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];
Johannes
sumber
OK, tapi tidak banyak bercerita tentang userInfo:nil. :)
Cœur
6

Anda dapat menggunakan dua metode untuk meningkatkan pengecualian di blok coba tangkap

@throw[NSException exceptionWithName];

atau metode kedua

NSException e;
[e raise];
Subbu
sumber
3

Saya percaya Anda tidak boleh menggunakan Pengecualian untuk mengontrol aliran program normal. Tetapi pengecualian harus dilemparkan setiap kali beberapa nilai tidak cocok dengan nilai yang diinginkan.

Misalnya jika beberapa fungsi menerima nilai, dan nilai itu tidak pernah diizinkan menjadi nol, maka boleh saja untuk membuat pengecualian daripada mencoba melakukan sesuatu yang 'pintar' ...

Ries

R. van Twisk
sumber
0

Anda hanya boleh melempar pengecualian jika Anda mendapati diri Anda dalam situasi yang menunjukkan kesalahan pemrograman, dan ingin menghentikan aplikasi agar tidak berjalan. Oleh karena itu, cara terbaik untuk melempar pengecualian adalah menggunakan makro NSAssert dan NSParameterAssert, dan memastikan bahwa NS_BLOCK_ASSERTIONS tidak ditentukan.

gnasher729
sumber
0

Contoh kode untuk case: @throw ([NSException exceptionWithName: ...

- (void)parseError:(NSError *)error
       completionBlock:(void (^)(NSString *error))completionBlock {


    NSString *resultString = [NSString new];

    @try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    resultString = dictFromData[@"someKey"];
    ...


} @catch (NSException *exception) {

      NSLog( @"Caught Exception Name: %@", exception.name);
      NSLog( @"Caught Exception Reason: %@", exception.reason );

    resultString = exception.reason;

} @finally {

    completionBlock(resultString);
}

}

Menggunakan:

[self parseError:error completionBlock:^(NSString *error) {
            NSLog(@"%@", error);
        }];

Kasus penggunaan lain yang lebih canggih:

- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock {

NSString *resultString = [NSString new];

NSException* customNilException = [NSException exceptionWithName:@"NilException"
                                                          reason:@"object is nil"
                                                        userInfo:nil];

NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException"
                                                                reason:@"object is not a NSNumber"
                                                              userInfo:nil];

@try {

    NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];

    if(!errorData.bytes) {

        @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
    }


    NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&error];

    NSArray * array = dictFromData[@"someArrayKey"];

    for (NSInteger i=0; i < array.count; i++) {

        id resultString = array[i];

        if (![resultString isKindOfClass:NSNumber.class]) {

            [customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException;

            break;

        } else if (!resultString){

            @throw customNilException;        // <======

            break;
        }

    }

} @catch (SomeCustomException * sce) {
    // most specific type
    // handle exception ce
    //...
} @catch (CustomException * ce) {
    // most specific type
    // handle exception ce
    //...
} @catch (NSException *exception) {
    // less specific type

    // do whatever recovery is necessary at his level
    //...
    // rethrow the exception so it's handled at a higher level

    @throw (SomeCustomException * customException);

} @finally {
    // perform tasks necessary whether exception occurred or not

}

}

Aleksandr B.
sumber
-7

Tidak ada alasan untuk tidak menggunakan pengecualian yang biasanya dalam tujuan C bahkan untuk menandakan pengecualian aturan bisnis. Apple bisa mengatakan menggunakan NSError yang peduli. Obj C telah ada sejak lama dan pada suatu waktu dokumentasi ALL C ++ mengatakan hal yang sama. Alasan mengapa itu tidak menjadi masalah seberapa mahal melempar dan menangkap pengecualian adalah, adalah seumur hidup pengecualian sangat pendek dan ... itu PENGECUALIAN untuk aliran normal. Saya belum pernah mendengar ada yang mengatakan dalam hidup saya, pria yang pengecualian butuh waktu lama untuk dilemparkan dan ditangkap.

Juga, ada orang yang berpikir bahwa tujuan C itu sendiri terlalu mahal dan kode dalam C atau C ++ sebagai gantinya. Jadi mengatakan selalu menggunakan NSError adalah informasi buruk dan paranoid.

Tetapi pertanyaan utas ini belum dijawab apa cara terbaik untuk melempar pengecualian. Cara untuk mengembalikan NSError sudah jelas.

Jadi apakah itu: [NSException boost: ... @throw [[alokasi NSException] initWithName .... atau @throw [[MyCustomException ...?

Saya menggunakan aturan yang dicentang / tidak dicentang di sini sedikit berbeda dari di atas.

Perbedaan nyata antara (menggunakan metafora java di sini) diperiksa / tidak dicentang penting -> apakah Anda dapat memulihkan dari pengecualian. Dan dengan memulihkan maksud saya bukan hanya TIDAK crash.

Jadi saya menggunakan kelas pengecualian khusus dengan @throw untuk pengecualian yang dapat dipulihkan, karena kemungkinan saya akan memiliki beberapa metode aplikasi untuk mencari jenis kegagalan tertentu dalam beberapa blok @catch. Misalnya jika aplikasi saya adalah mesin ATM, saya akan memiliki blok @catch untuk "WithdrawalRequestExceedBalanceException".

Saya menggunakan NSException: meningkatkan pengecualian runtime karena saya tidak memiliki cara untuk memulihkan dari pengecualian, kecuali untuk menangkapnya di tingkat yang lebih tinggi dan mencatatnya. Dan tidak ada gunanya membuat kelas khusus untuk itu.

Ngomong-ngomong itulah yang saya lakukan, tetapi jika ada cara yang lebih baik, sama ekspresifnya, saya juga ingin tahu. Dalam kode saya sendiri, sejak saya berhenti coding C hella sejak lama saya tidak pernah mengembalikan NSError bahkan jika saya melewati satu dengan API.

delete_user
sumber
4
Saya akan merekomendasikan mencoba untuk memprogram server dengan pengecualian sebagai bagian dari aliran normal kasus kesalahan sebelum membuat pernyataan generalisasi seperti "tidak ada alasan untuk tidak menggunakan pengecualian dalam objektif C". Percaya atau tidak, ada alasan untuk menulis aplikasi berkinerja tinggi (atau setidaknya bagian dari aplikasi) di ObjC, dan melempar pengecualian biasanya secara serius menghambat kinerja.
jbenet
6
Memang ada alasan yang sangat bagus mengapa tidak menggunakan pengecualian dalam Kakao. Lihat jawaban Bill Bumgarner di sini untuk informasi lebih lanjut: stackoverflow.com/questions/3378696/iphone-try-end-try/… . Dia tahu apa yang dia bicarakan (petunjuk: periksa majikannya). Pengecualian dalam Kakao diperlakukan sebagai kesalahan yang tidak dapat dipulihkan, dan dapat membuat sistem dalam kondisi tidak stabil. NSError adalah cara untuk mengatasi kesalahan umum.
Brad Larson
Pengecualian luar biasa . Kegagalan aturan bisnis pasti tidak memenuhi syarat. "Menemukan dan mendesain kode pengecualian-berat dapat menghasilkan kemenangan yang layak." MSDN melalui codinghorror.com/blog/2004/10/…
Jonathan Watmough
3
Pengecualian tidak dapat dibuang dari balok. Pengecualian yang dilemparkan ke lingkungan ARC dapat membuat program Anda bocor. Demikianlah downvote.
Moszi
"Tidak masalah seberapa mahal melempar dan menangkap pengecualian," Saya menulis sebuah emulator di mana kinerja sangat penting. Saya tidak bisa membuang banyak pengecualian mahal.
NobodyNada