Câu hỏi Xử lý lỗi "Sản xuất" dữ liệu lõi iPhone


Tôi đã nhìn thấy trong mã ví dụ được cung cấp bởi tài liệu tham khảo của Apple để làm thế nào bạn nên xử lý các lỗi dữ liệu lõi. I E:

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();
}

Nhưng không bao giờ có bất kỳ ví dụ nào về cách bạn Nên thực hiện nó.

Có ai có (hoặc có thể chỉ cho tôi theo hướng) một số thực tế "sản xuất" mã minh họa các phương pháp trên.

Cảm ơn trước, Matt


76
2018-02-14 20:49


gốc


+1 đây là một câu hỏi tuyệt vời. - Dave DeLong


Các câu trả lời:


Không ai sẽ cho bạn thấy mã sản xuất vì nó phụ thuộc 100% vào ứng dụng của bạn và nơi xảy ra lỗi.

Cá nhân, tôi đặt một tuyên bố khẳng định trong đó vì 99,9% thời gian lỗi này sẽ xảy ra trong quá trình phát triển và khi bạn sửa nó ở đó cao bạn sẽ không thấy nó trong sản xuất.

Sau khi xác nhận tôi sẽ trình bày một cảnh báo cho người dùng, hãy cho họ biết một lỗi không thể khôi phục đã xảy ra và ứng dụng sẽ thoát. Bạn cũng có thể đặt một blurb trong đó yêu cầu họ liên hệ với các nhà phát triển để bạn hy vọng có thể theo dõi này được thực hiện.

Sau đó tôi sẽ để lại hủy bỏ () trong đó vì nó sẽ "sụp đổ" các ứng dụng và tạo ra một dấu vết ngăn xếp mà bạn hy vọng có thể sử dụng sau này để theo dõi vấn đề.


32
2018-02-14 21:14



Marcus - Mặc dù khẳng định là tốt nếu bạn đang nói chuyện với một cơ sở dữ liệu sqlite hoặc tệp XML cục bộ, bạn cần một cơ chế xử lý lỗi mạnh mẽ hơn nếu lưu trữ liên tục của bạn dựa trên đám mây. - dar512
Nếu kho lưu trữ dữ liệu cốt lõi iOS của bạn dựa trên đám mây, bạn có vấn đề lớn hơn. - Marcus S. Zarra
Tôi không đồng ý với Apple về một số chủ đề. Đó là sự khác biệt giữa một tình huống giảng dạy (Apple) và trong các chiến hào (tôi). Từ một tình huống học tập, có bạn nên loại bỏ hủy bỏ. Trong thực tế, chúng rất hữu ích để nắm bắt các tình huống mà bạn không bao giờ tưởng tượng có thể. Các nhà văn tài liệu của Apple muốn giả vờ rằng mọi tình huống đều có trách nhiệm. 99,999% trong số đó là. Bạn làm gì cho thật sự bất ngờ? Tôi gặp sự cố và tạo nhật ký để tôi có thể tìm hiểu điều gì đã xảy ra. Đó là những gì hủy bỏ là cho. - Marcus S. Zarra
@cschuff, không ai trong số đó tác động đến dữ liệu cốt lõi -save: gọi điện. Tất cả những điều kiện này xảy ra lâu trước khi mã của bạn đạt đến điểm này. - Marcus S. Zarra
Đó là lỗi dự đoán có thể bị phát hiện và sửa chữa trước khi lưu. Bạn có thể hỏi Dữ liệu cốt lõi nếu dữ liệu hợp lệ và chính xác. Ngoài ra, bạn có thể kiểm tra tại thời điểm tiêu thụ để đảm bảo tất cả các trường hợp lệ đều có mặt. Đó là lỗi cấp nhà phát triển có thể được xử lý lâu trước khi -save: được gọi là. - Marcus S. Zarra


Đây là một phương pháp chung mà tôi đã đưa ra để xử lý và hiển thị các lỗi xác nhận trên iPhone. Nhưng Marcus là đúng: Bạn có thể muốn tinh chỉnh các thông điệp để thân thiện với người dùng hơn. Nhưng điều này ít nhất là cung cấp cho bạn một điểm khởi đầu để xem những gì lĩnh vực đã không xác nhận và tại sao.

- (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];
        }
    }
}

Thưởng thức.


31
2017-08-18 10:04



Chắc chắn không thể nhìn thấy bất cứ điều gì sai trái với mã này. Trông rắn. Cá nhân tôi thích xử lý các lỗi Core Data với một xác nhận. Tôi chưa thấy một sản phẩm được sản xuất nên tôi luôn coi chúng là lỗi phát triển hơn là lỗi sản xuất tiềm ẩn. Mặc dù đây chắc chắn là một mức độ bảo vệ khác :) - Marcus S. Zarra
Marcus, về xác nhận: Ý kiến ​​của bạn về việc giữ mã DRY về mặt xác nhận là gì? Theo ý kiến ​​của tôi, chỉ cần xác định tiêu chuẩn xác nhận của bạn một lần, trong mô hình (nơi nó thuộc về): Trường này không được để trống, trường đó phải dài ít nhất 5 ký tự và trường đó phải khớp với regex này . Cái đó Nên là tất cả thông tin cần thiết để hiển thị một thông điệp thích hợp cho người dùng. Nó bằng cách nào đó không ngồi tốt với tôi để làm những kiểm tra một lần nữa trong mã trước khi lưu MOC. Bạn nghĩ sao? - Johannes Fahrenkrug
Không bao giờ thấy nhận xét này vì nó không có trong câu trả lời của tôi. Ngay cả khi bạn đặt xác thực trong mô hình, bạn vẫn cần phải kiểm tra xem liệu đối tượng đã vượt qua xác thực và trình bày cho người dùng chưa. Tùy thuộc vào thiết kế có thể ở cấp trường (mật khẩu này là xấu, vv) hoặc tại điểm lưu. Lựa chọn của nhà thiết kế. Tôi sẽ không làm cho một phần của ứng dụng chung chung. - Marcus S. Zarra
@ MarcusS.Zarra Tôi đoán bạn không bao giờ có nó bởi vì tôi đã không chính xác @ -mention bạn :) Tôi nghĩ rằng chúng tôi hoàn toàn đồng ý: Tôi muốn xác nhận-thông tin ở trong mô hình, nhưng quyết định khi nào cò súng xác nhận và cách xử lý và trình bày kết quả xác nhận không nên là chung chung và phải được xử lý ở các vị trí thích hợp trong mã ứng dụng. - Johannes Fahrenkrug
Mã sẽ rất tuyệt. Câu hỏi duy nhất của tôi là, sau khi hiển thị cảnh báo hoặc ghi nhật ký phân tích, tôi có nên khôi phục ngữ cảnh Dữ liệu cốt lõi hoặc hủy bỏ ứng dụng không? Nếu không, tôi đoán rằng các thay đổi chưa lưu sẽ tiếp tục gây ra sự cố tương tự khi bạn cố gắng lưu lại. - Jake


Tôi tìm thấy chức năng tiết kiệm phổ biến này là một giải pháp tốt hơn nhiều:

- (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;
}

Bất cứ khi nào một lưu thất bại này sẽ rollback NSManagedObjectContext của bạn có nghĩa là nó sẽ thiết lập lại tất cả các thay đổi đã được thực hiện trong bối cảnh kể từ lần lưu cuối cùng. Vì vậy, bạn phải cẩn thận để luôn luôn thay đổi liên tục bằng cách sử dụng chức năng lưu ở trên càng sớm càng tốt vì bạn có thể dễ dàng mất dữ liệu khác.

Để chèn dữ liệu, đây có thể là một biến thể lỏng lẻo hơn cho phép các thay đổi khác tồn tại trên:

- (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;
}

Lưu ý: Tôi đang sử dụng CocoaLumberjack để đăng nhập tại đây.

Bất kỳ bình luận nào về cách cải thiện điều này càng được hoan nghênh!

BR Chris


5
2017-09-03 12:05



Tôi nhận được hành vi lạ khi tôi cố gắng sử dụng rollback để đạt được điều này: stackoverflow.com/questions/34426719/… - malhal
Tôi đang sử dụng hoàn tác thay thế ngay bây giờ - malhal


Tôi ngạc nhiên không có ai ở đây thực sự xử lý lỗi theo cách nó được xử lý. Nếu bạn nhìn vào tài liệu, bạn sẽ thấy.

Lý do điển hình cho một lỗi ở đây bao gồm: * Thiết bị đã hết   Không gian. * Cửa hàng liên tục không thể truy cập được, do   quyền hoặc bảo vệ dữ liệu khi thiết bị bị khóa. * Các   không thể di chuyển cửa hàng sang phiên bản mô hình hiện tại. * Các   thư mục mẹ không tồn tại, không thể được tạo hoặc không cho phép   viết.

Vì vậy, nếu tôi tìm thấy lỗi khi thiết lập ngăn xếp dữ liệu lõi, tôi trao đổi rootViewController của UIWindow và hiển thị giao diện người dùng cho người dùng biết rằng thiết bị của họ có thể đầy hoặc cài đặt bảo mật của họ quá cao để Ứng dụng hoạt động. Tôi cũng cung cấp cho họ một nút 'thử lại', vì vậy họ có thể cố gắng khắc phục sự cố trước khi ngăn xếp dữ liệu lõi được thử lại.

Ví dụ: người dùng có thể giải phóng một số dung lượng lưu trữ, quay lại Ứng dụng của tôi và nhấn nút thử lại.

Khẳng định? Có thật không? Quá nhiều nhà phát triển trong phòng!

Tôi cũng ngạc nhiên bởi số lượng các hướng dẫn trực tuyến mà không đề cập đến cách hoạt động tiết kiệm có thể thất bại vì những lý do này. Vì vậy, bạn sẽ cần phải đảm bảo rằng bất kỳ sự kiện lưu ANYWHERE trong ứng dụng của bạn có thể thất bại vì thiết bị JUST NÀY MINUTE đã trở thành đầy đủ với các ứng dụng của bạn tiết kiệm tiết kiệm tiết kiệm.


4
2018-06-24 09:32



Câu hỏi này là về việc tiết kiệm trong ngăn xếp dữ liệu cốt lõi, nó không phải là về việc thiết lập ngăn xếp dữ liệu cốt lõi. Nhưng tôi đồng ý tiêu đề của nó có thể gây hiểu lầm và có thể nó phải được sửa đổi. - valeCocoa
Tôi không đồng ý @valeCocoa. Bài đăng rõ ràng về cách xử lý các lỗi lưu trong sản xuất. Hãy nhìn lại.
@roddanash đó là những gì tôi đã nói ... WtH! :) Hãy nhìn vào câu trả lời của bạn. - valeCocoa
Bạn điên bro
bạn dán một phần của tài liệu cho các lỗi có thể xảy ra trong khi instantiating lưu trữ liên tục trên một câu hỏi liên quan đến các lỗi xảy ra trong khi tiết kiệm bối cảnh, và tôi là một trong những điên? Được… - valeCocoa


Tôi đã thực hiện một phiên bản Swift của câu trả lời hữu ích của @JohannesFahrenkrug có thể hữu ích:

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