Pertanyaan Penanganan Kesalahan "Produksi" Data Inti iPhone


Saya telah melihat dalam kode contoh yang diberikan oleh referensi Apple tentang bagaimana Anda harus menangani kesalahan Data Inti. Yaitu:

NSError *error = nil;
if (![context save:&error]) {
/*
 Replace this implementation with code to handle the error appropriately.

 abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
 */
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

Tetapi tidak pernah ada contoh bagaimana Anda harus terapkan itu.

Adakah yang punya (atau dapat mengarahkan saya ke arah) beberapa kode "produksi" aktual yang menggambarkan metode di atas.

Terima kasih sebelumnya, Matt


76
2018-02-14 20:49


asal


Jawaban:


Tidak ada yang akan menunjukkan kepada Anda kode produksi karena itu tergantung 100% pada aplikasi Anda dan di mana kesalahan terjadi.

Secara pribadi, saya menaruh pernyataan tegas di sana karena 99,9% dari waktu kesalahan ini akan terjadi dalam pengembangan dan ketika Anda memperbaikinya ada itu sangat tidak mungkin Anda akan melihatnya dalam produksi.

Setelah menegaskan saya akan memberikan peringatan kepada pengguna, biarkan mereka tahu kesalahan yang tidak dapat diperbaiki terjadi dan bahwa aplikasi akan keluar. Anda juga dapat menempatkan uraian di sana meminta mereka untuk menghubungi pengembang sehingga Anda semoga dapat melacak ini dilakukan.

Setelah itu saya akan meninggalkan batalkan () di sana karena akan "menabrak" aplikasi dan menghasilkan jejak stack yang dapat Anda harapkan nanti untuk melacak masalah.


32
2018-02-14 21:14



Ini adalah salah satu metode umum yang saya gunakan untuk menangani dan menampilkan kesalahan validasi pada iPhone. Tapi Marcus benar: Anda mungkin ingin mengubah pesan agar lebih ramah pengguna. Tetapi ini setidaknya memberi Anda titik awal untuk melihat bidang apa yang tidak memvalidasi dan mengapa.

- (void)displayValidationError:(NSError *)anError {
    if (anError && [[anError domain] isEqualToString:@"NSCocoaErrorDomain"]) {
        NSArray *errors = nil;

        // multiple errors?
        if ([anError code] == NSValidationMultipleErrorsError) {
            errors = [[anError userInfo] objectForKey:NSDetailedErrorsKey];
        } else {
            errors = [NSArray arrayWithObject:anError];
        }

        if (errors && [errors count] > 0) {
            NSString *messages = @"Reason(s):\n";

            for (NSError * error in errors) {
                NSString *entityName = [[[[error userInfo] objectForKey:@"NSValidationErrorObject"] entity] name];
                NSString *attributeName = [[error userInfo] objectForKey:@"NSValidationErrorKey"];
                NSString *msg;
                switch ([error code]) {
                    case NSManagedObjectValidationError:
                        msg = @"Generic validation error.";
                        break;
                    case NSValidationMissingMandatoryPropertyError:
                        msg = [NSString stringWithFormat:@"The attribute '%@' mustn't be empty.", attributeName];
                        break;
                    case NSValidationRelationshipLacksMinimumCountError:  
                        msg = [NSString stringWithFormat:@"The relationship '%@' doesn't have enough entries.", attributeName];
                        break;
                    case NSValidationRelationshipExceedsMaximumCountError:
                        msg = [NSString stringWithFormat:@"The relationship '%@' has too many entries.", attributeName];
                        break;
                    case NSValidationRelationshipDeniedDeleteError:
                        msg = [NSString stringWithFormat:@"To delete, the relationship '%@' must be empty.", attributeName];
                        break;
                    case NSValidationNumberTooLargeError:                 
                        msg = [NSString stringWithFormat:@"The number of the attribute '%@' is too large.", attributeName];
                        break;
                    case NSValidationNumberTooSmallError:                 
                        msg = [NSString stringWithFormat:@"The number of the attribute '%@' is too small.", attributeName];
                        break;
                    case NSValidationDateTooLateError:                    
                        msg = [NSString stringWithFormat:@"The date of the attribute '%@' is too late.", attributeName];
                        break;
                    case NSValidationDateTooSoonError:                    
                        msg = [NSString stringWithFormat:@"The date of the attribute '%@' is too soon.", attributeName];
                        break;
                    case NSValidationInvalidDateError:                    
                        msg = [NSString stringWithFormat:@"The date of the attribute '%@' is invalid.", attributeName];
                        break;
                    case NSValidationStringTooLongError:      
                        msg = [NSString stringWithFormat:@"The text of the attribute '%@' is too long.", attributeName];
                        break;
                    case NSValidationStringTooShortError:                 
                        msg = [NSString stringWithFormat:@"The text of the attribute '%@' is too short.", attributeName];
                        break;
                    case NSValidationStringPatternMatchingError:          
                        msg = [NSString stringWithFormat:@"The text of the attribute '%@' doesn't match the required pattern.", attributeName];
                        break;
                    default:
                        msg = [NSString stringWithFormat:@"Unknown error (code %i).", [error code]];
                        break;
                }

                messages = [messages stringByAppendingFormat:@"%@%@%@\n", (entityName?:@""),(entityName?@": ":@""),msg];
            }
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Validation Error" 
                                                            message:messages
                                                           delegate:nil 
                                                  cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
            [alert show];
            [alert release];
        }
    }
}

Nikmati.


31
2017-08-18 10:04



Saya menemukan fungsi penyimpanan umum ini solusi yang jauh lebih baik:

- (BOOL)saveContext {
    NSError *error;
    if (![self.managedObjectContext save:&error]) {
        DDLogError(@"[%@::%@] Whoops, couldn't save managed object context due to errors. Rolling back. Error: %@\n\n", NSStringFromClass([self class]), NSStringFromSelector(_cmd), error);
        [self.managedObjectContext rollback];
        return NO;
    }
    return YES;
}

Setiap kali menyimpan gagal ini akan me-rollback NSManagedObjectContext Anda yang berarti itu akan mengatur ulang semua perubahan yang telah dilakukan dalam konteks sejak terakhir menyimpan. Jadi Anda harus berhati-hati untuk selalu mempertahankan perubahan menggunakan fungsi penyimpanan di atas sedini dan sesering mungkin karena Anda mungkin kehilangan data dengan mudah.

Untuk memasukkan data, ini mungkin menjadi varian yang lebih longgar yang memungkinkan perubahan lain untuk ditayangkan:

- (BOOL)saveContext {
    NSError *error;
    if (![self.managedObjectContext save:&error]) {
        DDLogError(@"[%@::%@] Whoops, couldn't save. Removing erroneous object from context. Error: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), object.objectId, error);
        [self.managedObjectContext deleteObject:object];
        return NO;
    }
    return YES;
}

Catatan: Saya menggunakan CocoaLumberjack untuk masuk di sini.

Setiap komentar tentang cara meningkatkan ini lebih diterima!

BR Chris


5
2017-09-03 12:05



Saya terkejut tidak ada orang di sini yang sebenarnya menangani kesalahan seperti yang seharusnya ditangani. Jika Anda melihat dokumentasi, Anda akan melihat.

Alasan umum untuk kesalahan di sini termasuk: * Perangkat mati   ruang. * Toko persisten tidak dapat diakses, karena   perizinan atau perlindungan data saat perangkat terkunci. * The   toko tidak dapat dipindahkan ke versi model saat ini. * The   direktori induk tidak ada, tidak dapat dibuat, atau tidak diizinkan   penulisan.

Jadi, jika saya menemukan kesalahan saat menyiapkan tumpukan data inti, saya menukar rootViewController dari UIWindow dan menunjukkan UI yang dengan jelas memberitahu pengguna bahwa perangkat mereka mungkin penuh, atau pengaturan keamanan mereka terlalu tinggi untuk aplikasi ini berfungsi. Saya juga memberi mereka tombol 'coba lagi', sehingga mereka dapat mencoba untuk memperbaiki masalah sebelum tumpukan data inti dicoba kembali.

Misalnya pengguna dapat mengosongkan beberapa ruang penyimpanan, kembali ke Aplikasi saya dan tekan tombol coba lagi.

Menegaskan? Sangat? Terlalu banyak pengembang di ruangan!

Saya juga terkejut dengan jumlah tutorial online yang tidak menyebutkan bagaimana operasi penyimpanan bisa gagal karena alasan ini juga. Jadi Anda perlu memastikan bahwa semua acara yang disimpan di mana pun dalam Aplikasi Anda bisa gagal karena perangkat HANYA MENIT INI menjadi penuh dengan Aplikasi Anda menyimpan tabungan.


4
2018-06-24 09:32



Saya telah membuat versi Swift dari jawaban berguna @JohannesFahrenkrug yang dapat berguna:

public func displayValidationError(anError:NSError?) -> String {
    if anError != nil && anError!.domain.compare("NSCocoaErrorDomain") == .OrderedSame {
        var messages:String = "Reason(s):\n"
        var errors = [AnyObject]()
        if (anError!.code == NSValidationMultipleErrorsError) {
            errors = anError!.userInfo[NSDetailedErrorsKey] as! [AnyObject]
        } else {
            errors = [AnyObject]()
            errors.append(anError!)
        }
        if (errors.count > 0) {
            for error in errors {
                if (error as? NSError)!.userInfo.keys.contains("conflictList") {
                    messages =  messages.stringByAppendingString("Generic merge conflict. see details : \(error)")
                }
                else
                {
                    let entityName = "\(((error as? NSError)!.userInfo["NSValidationErrorObject"] as! NSManagedObject).entity.name)"
                    let attributeName = "\((error as? NSError)!.userInfo["NSValidationErrorKey"])"
                    var msg = ""
                    switch (error.code) {
                    case NSManagedObjectValidationError:
                        msg = "Generic validation error.";
                        break;
                    case NSValidationMissingMandatoryPropertyError:
                        msg = String(format:"The attribute '%@' mustn't be empty.", attributeName)
                        break;
                    case NSValidationRelationshipLacksMinimumCountError:
                        msg = String(format:"The relationship '%@' doesn't have enough entries.", attributeName)
                        break;
                    case NSValidationRelationshipExceedsMaximumCountError:
                        msg = String(format:"The relationship '%@' has too many entries.", attributeName)
                        break;
                    case NSValidationRelationshipDeniedDeleteError:
                        msg = String(format:"To delete, the relationship '%@' must be empty.", attributeName)
                        break;
                    case NSValidationNumberTooLargeError:
                        msg = String(format:"The number of the attribute '%@' is too large.", attributeName)
                        break;
                    case NSValidationNumberTooSmallError:
                        msg = String(format:"The number of the attribute '%@' is too small.", attributeName)
                        break;
                    case NSValidationDateTooLateError:
                        msg = String(format:"The date of the attribute '%@' is too late.", attributeName)
                        break;
                    case NSValidationDateTooSoonError:
                        msg = String(format:"The date of the attribute '%@' is too soon.", attributeName)
                        break;
                    case NSValidationInvalidDateError:
                        msg = String(format:"The date of the attribute '%@' is invalid.", attributeName)
                        break;
                    case NSValidationStringTooLongError:
                        msg = String(format:"The text of the attribute '%@' is too long.", attributeName)
                        break;
                    case NSValidationStringTooShortError:
                        msg = String(format:"The text of the attribute '%@' is too short.", attributeName)
                        break;
                    case NSValidationStringPatternMatchingError:
                        msg = String(format:"The text of the attribute '%@' doesn't match the required pattern.", attributeName)
                        break;
                    default:
                        msg = String(format:"Unknown error (code %i).", error.code) as String
                        break;
                    }

                    messages = messages.stringByAppendingString("\(entityName).\(attributeName):\(msg)\n")
                }
            }
        }
        return messages
    }
    return "no error"
}`

2
2017-08-25 07:35