繁体   English   中英

哪个是检查Objective-C中方法参数的最佳方法?

[英]Which is the best way to check method arguments in Objective-C?

编码方法或函数时,检查输入参数以响应任何可能的失败方案是一种很好的做法。

例如:

-(void)insertNameInDictionary:(NSString*)nameString
{
    [myDictionary setObject:nameString forKey:@"Name"];
}

这看起来不错,但如果nameStringnil ,则应用程序将崩溃。 所以,我们可以检查它是否nil ,对吧? 我们还可以检查它是否是NSString而不是NSNumber,或者它是否响应我们的方法需要调用的方法。

所以我的问题是:检查这些论点最完整,最优雅的方法是什么?

有多种方法可以实现这种保护:

  • NSParameterAssert
  • __attribute__((nonnull))
  • 和一个简单的if测试。

[编辑]自最新的Xcode版本(Xcode 6)以来,Apple添加了可空性注释 ,这是另一种 - 更好 - 表达参数是否nil / null 您应该迁移到该表示法而不是使用__attribute__((nonnull))来使您的API更具可读性。


详情如下:

  1. NSParameterAssert是Apple的专用宏,用于检查方法参数的条件,如果失败则抛出专用异常。

    • 仅在运行时提供保护
    • 这个宏仍然认为传递nil作为参数是一个编程/概念错误 ,考虑到由于你的应用程序工作流程,它通常应该永远不会发生(由于其他条件确保param永远不会是nil ),如果它不是真的出错。
    • 它仍然会产生一个异常(你的调用代码可能会在必要时使用@try/@catch ),但它有一个优点,即抛出的异常更明确(告诉参数预期不会是nil而不是与丑陋的崩溃)难以理解的callstack / message)。
  2. 如果你想让你的代码用nil调用你的方法但在这种情况下什么也不做,你可以简单地在函数/方法的开头if (!param) return

    • 这认为传递nil不是编程/概念错误,因此有时可能由于应用程序的工作流程而发生,因此这是一个可以发生并且不应该崩溃的可接受的情况。
  3. 不太常见的__attribute__((nonnull)) GCC / LLVM属性专用于告诉编译器函数/方法的某些参数应该不为null 这样,如果编译器在编译时可以检测到您尝试使用nil / NULL参数调用方法/函数(比如直接调用insertNameInDitionary:nil而不是使用在编译时无法确定该值的变量) ,它会立即发出编译错误,让你尽快修复它。

  4. [编辑]从最新的Xcode 6开始,您可以(并且应该)使用可空性注释而不是__attribute__((nonnull)) 请参阅Apple博客文章中的示例。


总结如下:

如果你想标记你的方法,希望你的参数是不nil ,从而表明随着称之为nil是一个逻辑错误,你应该做到以下几点:

- (void)insertNameInDictionary:(NSString*)nameString __attribute__((nonnull))
{
    // __attribute__((nonnull)) allows to check obvious cases (directly passing nil) at compile time
    NSParameterAssert(nameString); // NSParameterAssert allows to check other cases (passing a variable that may or may not be nil) at runtime
    [myDictionary setObject:nameString forKey:@"Name"];
}

如果你认为nil调用你的方法可以发生并且是可以接受的 ,并且你只是想在这些情况下避免崩溃,那么只需执行以下操作:

-(BOOL)insertNameInDictionary:(NSString*)nameString
{
    if (nameString == nil) return NO;
    [myDictionary setObject:nameString forKey:@"Name"];
    return YES;
}

如果您认为您应该能够在字典中插入nil对象 ,则可以在该特定情况下将nil值转换为NSNull ,以便插入专用于此类用法的NSNull单例(我使用的是使用?:三元运算符使代码更紧凑):

-(void)insertNameInDictionary:(NSString*)nameString
{
    [myDictionary setObject:nameString?:[NSNull null] forKey:@"Name"];
}

最后一种情况,如果你想在那个特定的例子中传递nil只是从myDictionary 删除 Name ,你可以简单地做一个简单的if测试并调用removeObjectForKey:@"Name"如果nameStringnil并且调用setObject:forKey: if if's不...或者你可以使用KVC和泛型setValue:forKey: KVC方法,根本不特定于NSDictionary,所以不要与setObject:forKey:混淆,在NSDictionary的情况下确实具有相同的行为(删除如果我们为值参数传递nil ,则从字典中键入。

- (void)insertNameInDictionary:(NSString *)nameString
{
    if ([nameString isKindOfClass:[NSString class]]) {
        [myDictionary setObject:nameString forKey:@"Name"];
    }
}

Apple建议使用NSAssert

NSAssert(nameString, @"nil nameString is not allowed");

此断言将终止您的程序,并生成一条错误消息,说明发生了什么。

上面, != nil部分是隐式的,因为Objective-C允许它。 您可以明确说明它以获得更好的可读性:

NSAssert(nameString != nil, @"nil nameString is not allowed");

断言不仅限于nil检查,因为它们可以采取任意复杂的条件。 您可以检查参数是否为预期类型,它是否响应特定选择器,依此类推。 可以在发布代码中禁用断言以节省CPU周期和电池。

我在这看到几个问题。 NSMutableDictionary没有“ setObject ”方法。 您需要指定一个键。

-(void)insertNameInDictionary:(NSString*)nameString
{
    if(nameString)
        [myDictionary setObject:nameString forKey: @"someKey"];
}

你也可以通过我的例子中的“ if(nameString) ”位进行零检查。 如果你想要一个空字符串,请使用@""而不是nil。

这一切都取决于你想要发生什么的语义,如果你想要删除KV对,如果值为nil,那么你可以使用setValue:forKey:

喜欢:

-(void)insertNameInDictionary:(NSString*)nameString
{
    [myDictionary setValue:nameString forKey:@"Name"];
}

或者如果要存储表示nil值的值,可以使用NSNull

-(void)insertNameInDictionary:(NSString*)nameString
{
    [myDictionary setObject:nameString? :[NSNull null] forKey:@"Name"];
}

-(NSString *)nameInDictionary
{
    NSString * retVal = [myDictionary objectForKey:@"Name"];
    return (retVal==[NSNull null]) ? nil : retVal;
}

或者您可能只想记录它。

-(void)insertNameInDictionary:(NSString*)nameString
{
    if(!nameString)
    {
        NSLog(@"expected nameString to be not null... silly me %s",__PRETTY_FUNCTION__);
        return;
    }
    [myDictionary setObject:nameString forKey:@"Name"];
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM