iPhone核心数据"生产"错误处理

Swa*_*way 83 iphone error-handling core-data

我已经在Apple引用的示例代码中看到了如何处理Core Data错误.即:

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();
}
Run Code Online (Sandbox Code Playgroud)

但从来没有的你怎么什么例子应该实现它.

有没有人(或可以指向我的方向)一些实际的"生产"代码,说明上述方法.

先谢谢,马特

Mar*_*rra 32

没有人会向您展示生产代码,因为它取决于您的应用程序以及发生错误的位置.

就个人而言,我在其中放置了一个断言声明,因为99.9%的时间会在开发过程中发生此错误,当您在那里修复它时,您可能不会在生产中看到它.

在断言之后,我会向用户发出警报,让他们知道发生了不可恢复的错误,并且应用程序将要退出.您还可以在其中放置一个模糊信息,要求他们联系开发人员,以便您可以跟踪完成此操作.

之后我会将abort()留在那里,因为它会"崩溃"应用程序并生成一个堆栈跟踪,您可以在以后使用它来跟踪问题.

  • 如果您的iOS Core Data持久存储是基于云的,那么您就会遇到更大的问题. (4认同)
  • 我在一些主题上不同意Apple.这是教学情境(Apple)和战壕(我)之间的区别.从学术情况来看,是的,你应该删除中止.实际上,它们对于捕捉您从未想象过的情况非常有用.Apple文档编写者喜欢假装每种情况都是负责任的.其中99.999%是.你为真正的意外做了什么?我崩溃并生成一个日志,所以我可以找出发生了什么.这就是堕胎的目的. (3认同)
  • 这是一个可以在保存之前捕获并纠正的预期错误.您可以询问Core Data数据是否有效并更正.另外,您可以在消费时测试,以确保所有有效字段都存在.这是一个开发人员级别的错误,可以在调用`-save:`之前很久就处理掉. (3认同)

Joh*_*rug 31

这是我提出的处理和显示iPhone验证错误的一种通用方法.但马库斯是对的:你可能想要调整消息以使用户更友好.但这至少为您提供了一个起点,看看哪个领域没有验证,为什么.

- (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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请享用.

  • 当然看不出这个代码有什么问题.看起来很稳 我个人更喜欢用断言来处理核心数据错误.我还没有看到有人将其投入生产,所以我一直认为它们是开发错误而不是潜在的生产错误.虽然这肯定是另一个保护级别:) (3认同)
  • Marcus,关于断言:您对在验证方面保持代码DRY有何看法?在我看来,非常希望在模型中(它所属的位置)只定义一次验证标准:此字段不能为空,该字段必须至少为5个字符且该字段必须与此正则表达式匹配.那个_should_是向用户显示适当的消息所需的所有信息.在保存MOC之前,它在某种程度上不适合我在代码中再次执行这些检查.你怎么看? (2认同)
  • 从来没有看过这个评论,因为它不是我的回答.即使您在模型中放置验证,您仍需要检查对象是否通过验证并将其呈现给用户.取决于可能在现场级别(此密码不良等)或保存点的设计.设计师的选择.我不会让应用程序的那部分通用. (2认同)

小智 7

我很惊讶这里没有人真正按照它的处理方式处理错误.如果你查看文档,你会看到.

此处出现错误的典型原因包括:*设备空间不足.*由于设备锁定时的权限或数据保护,持久存储无法访问.*商店无法迁移到当前的模型版本.*父目录不存在,无法创建或不允许写入.

因此,如果我在设置核心数据堆栈时发现错误,我会交换UIWindow的rootViewController并显示UI,清楚地告诉用户他们的设备可能已满,或者他们的安全设置太高而无法运行此应用程序.我还给他们一个'再试一次'按钮,这样他们就可以在重新尝试核心数据堆栈之前尝试解决问题.

例如,用户可以释放一些存储空间,返回我的应用程序并再次按下尝试按钮.

断言?真?房间里的开发人员太多了!

我也对在线教程的数量感到惊讶,因为这些原因也未提及保存操作如何失败.因此,您需要确保应用程序中的任何保存事件都可能失败,因为设备JUST THIS MINUTE已满,节省了您的Apps节省.


csc*_*uff 5

我发现这个常见的保存功能是一个更好的解决方案

- (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;
}
Run Code Online (Sandbox Code Playgroud)

每当保存失败时,这将回滚NSManagedObjectContext,这意味着它将重置自上次保存以来在上下文中执行的所有更改.因此,您必须尽可能早地和定期地使用上述保存功能仔细注意始终保持更改,否则您可能很容易丢失数据.

对于插入数据,这可能是一个更宽松的变体,允许其他更改生效:

- (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;
}
Run Code Online (Sandbox Code Playgroud)

注意:我在这里使用CocoaLumberjack进行日志记录.

任何有关如何改进这一点的评论都是值得欢迎的!

BR克里斯