Apa cara terbaik untuk melempar pengecualian dalam objektif-c / kakao?
Apa cara terbaik untuk melempar pengecualian dalam objektif-c / kakao?
Jawaban:
Saya menggunakan [NSException raise:format:]
sebagai berikut:
[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
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 .
@throw([NSException exceptionWith…])
Xcode mengenali @throw
pernyataan sebagai titik keluar fungsi, seperti return
pernyataan. Menggunakan @throw
sintaks menghindari peringatan salah " Kontrol dapat mencapai akhir fungsi tidak-kosong " yang mungkin Anda peroleh [NSException raise:…]
.
Juga, @throw
dapat digunakan untuk melempar objek yang bukan dari kelas NSException.
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.
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
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.
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
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];
userInfo:nil
. :)
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
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.
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
}
}
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.
@throw([NSException exceptionWith…])
pendekatan pendekatan karena lebih ringkas.