在我的自定义框架中,我有一个如下所示的方法,它从字典中获取值并将其转换为BOOL并返回布尔值。
- (BOOL)getBoolValueForKey:(NSString *)key;
如果此方法的调用者传递了不存在的键,该怎么办?我应该抛出一个自定义的NSException,说密钥不存在(但是在目标c中不建议抛出异常)或者将NSError参数添加到此方法中,如下所示?
- (BOOL)getBoolValueForKey:(NSString *)key error:(NSError **)error;
如果我使用NSError,我将不得不返回' NO'这将是误导,因为' NO'可以是任何有效密钥的有效值。
答案 0 :(得分:4)
此问题的API由NSUserDefaults
构建,应该是您设计API的起点:
- (BOOL)boolForKey:(NSString *)defaultName;
如果布尔值与用户默认值中的defaultName相关联,则返回该值。否则,返回NO。
除非你有充分的理由,否则你应该避免创建一个不同的API来从密钥库中获取bool。在大多数ObjC接口中,获取非exixtant键返回nil
并且nil
在布尔上下文中被解释为NO
。
传统上,如果想要区分NO
和nil
,请致电objectForKey
以检索NSNumber
并检查nil
。同样,这是许多Cocoa密钥库的行为,不应轻易改变。
然而,有可能有一个强烈的理由违反这种预期的模式(在这种情况下,你应该在文档中仔细注意,因为它是令人惊讶的)。在这种情况下,有几种完善的模式。
首先,您可以考虑将未知密钥作为编程错误提取,并且您应该抛出异常,期望程序很快就会崩溃。为此创建新的异常是非常不寻常的(并且是意外的)。你应该提出NSInvalidArgumentException
,它确实存在于这个问题中。
其次,您可以通过正确使用nil
方法来区分NO
和get
。您的方法以get
开头,但不应该。 get
表示Cocoa中的“按引用返回”,您可以这样使用它。像这样:
- (BOOL)getBool:(BOOL *)value forKey:(NSString *)key {
id result = self.values[key];
if (result) {
if (value) {
// NOTE: This throws an exception if result exists, but does not respond to
// boolValue. That's intentional, but you could also check for that and return
// NO in that case instead.
*value = [result boolValue];
}
return YES;
}
return NO;
}
这将获取指向bool的指针,并在值可用时将其填入,并返回YES
。如果该值不可用,则返回NO
。
没有理由让NSError
参与进来。这增加了复杂性而没有提供任何价值。即使您正在考虑使用Swift桥接,我也不会在此使用NSError
来获取throws
。相反,你应该围绕这个返回Bool?
的方法编写一个简单的Swift包装器。这是一个更强大的方法,在Swift方面使用起来更简单。
答案 1 :(得分:3)
如果你希望传递一个不存在的密钥作为程序员错误,即在运行时实际上永远不会发生的事情,因为例如某些上游应该已经处理过这种可能性,那么断言失败或NSException就是这种方式去做吧。引用Exception Programming Guide中的Apple文档:
您应该保留使用编程异常或意外的运行时错误,例如越界集合访问,尝试改变不可变对象,发送无效消息以及丢失与窗口服务器的连接。在创建应用程序时而不是在运行时,通常会使用异常处理这些类型的错误。
如果您希望传达程序可以恢复/可以继续执行的运行时错误,那么添加错误指针就是这样做的。
原则上,即使存在非严重错误情况,也可以使用BOOL
作为返回类型。但是,如果您打算使用Swift中的代码进行交互,则会出现这种情况:
NO
总是暗示会抛出错误,即使您在Objective-C方法实现中没有填充错误指针,即您需要执行/捕获和处理特定的零错误。 如果你打算从Swift中使用这个API,我可能会把BOOL打包成一个可以为空的NSNumber(在这种情况下,错误情况将是nil,而成功的NO情况将是一个NSNumber,其中包含NO)
我应该注意,对于潜在可用的setter的特定情况,您应该遵循强大的约定as noted in one of the other answers。
答案 2 :(得分:1)
您可以找出苹果错误处理方法的主要弱点。
我们通过在成功案例中保证NSError
为nil
来处理这些情况,因此您实际检查错误:
if (error) {
// ... problem
// handle error and/ or return
}
由于这与Apples错误句柄相矛盾,Error
永远不能保证nil
,但在失败的情况下保证不nil
,受影响的方法必须有详细记录对客户了解这种特殊行为。
这不是一个好的解决方案,但我知道的最好。
(这是我们在swift中不再需要处理的令人讨厌的事情之一)
答案 3 :(得分:0)
如果你想要所有这些
我建议做一个基于块的实现。您将successBlock
和errorBlock
明确分开。
来电者会调用这样的方法
[self getBoolValueForKey:@"key" withSuccessBlock:^(BOOL value) {
[self workWithKeyValue:value];
} andFailureBlock:^(NSError *error) {
NSLog(@"error: %@", error.localizedFailureReason);
}];
和实施:
- (void)getBoolValueForKey:(NSString *)key withSuccessBlock:(void (^)(BOOL value))success andFailureBlock:(void (^)(NSError *error))failure {
BOOL errorOccurred = ...
if (errorOccurred) {
// userInfo will change
// if there are multiple failure conditions to distinguish between
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The operation timed out.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Have you tried turning it off and on again?", nil)
};
NSError *error = [NSError errorWithDomain:@"domain" code:999 userInfo:userInfo];
failure(error);
return;
}
BOOL boolValue = ...
success(boolValue);
}
答案 4 :(得分:0)
我们使用这个
- (id) safeObjectForKey:(NSString*)key {
id retVal = nil;
if ([self objectForKey:key] != nil) {
retVal = [self objectForKey:key];
} else {
ALog(@"*** Missing key exception prevented by safeObjectForKey");
}
return retVal;
}
头文件NSDictionary + OurExtensions.h
#import <Foundation/Foundation.h>
@interface NSDictionary (OurExtensions)
- (id) safeObjectForKey:(NSString*)key;
@end
答案 5 :(得分:0)
在这种情况下,如果调用方传递不存在的密钥,我宁愿返回NSInteger并返回0
,1
和NSNotFound
。
从这种方法的本质来看,处理NSNorFound
应该是来电者的判断。正如我所看到的,从方法名称返回错误对用户来说并不是很令人鼓舞。