在Objective-C中检查方法参数的最佳方法是哪种? [英] Which is the best way to check method arguments in Objective-C?

查看:126
本文介绍了在Objective-C中检查方法参数的最佳方法是哪种?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编码方法或函数时,最好检查输入参数以响应任何可能的失败情况。



例如:

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

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



所以我的问题是:检查这些参数的最完整和最优雅的方法是什么?

解决方案

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




  • NSParameterAssert

  • __ attribute __((nonnull))

  • 和一个简单的 if test。




自最新的Xcode版本(Xcode 6)以来,Apple增加了可空性注释,这是另一个 - 而且更好 - 表达参数是否可以 nil / null 的方式。您应该迁移到该表示法而不是使用 __ attribute __((nonnull))来使您的API更具可读性。






详细信息:


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




    • 这是一个仅在运行时提供保护

    • 此宏仍然认为传递 nil 作为参数是编程/构思错误,考虑到由于您的应用程序工作流程,它通常不会发生(由于其他条件确保参数永远不会 nil ),以及如果它不是真的出错了。

    • 它仍然会产生异常(如果需要,您的调用代码可能 @ try / @ catch ),但它的优点是抛出的异常更明确(告诉参数ter预计不会是 nil 而不是使用丑陋且难以理解的callstack / message崩溃。


  2. 如果你想允许你的代码用 nil 来调用你的方法,但什么都不做在这种情况下,您可以简单地 if(!param)在函数/方法的开头返回




    • 这认为传递 nil 不是编程/概念错误,因此有时会因应用程序的工作流程而发生,因此这是一个可接受的案例这可能发生而且不应该崩溃。


  3. 鲜为人知, __属性__((非空)) GCC / LLVM属性 专用于告诉编译器函数/方法的某些参数应该是非空即可。这样,如果编译器可以在编译时检测到您尝试使用 nil / NULL 参数(比如直接调用 insertNameInDitionary:nil 而不是使用在编译时无法确定哪个值的变量),它会立即发出编译错误让你尽快修复它。


  4. 从最新的Xcode 6开始,你可以(而且应该)使用可侵犯性注释而不是 __ attribute __((nonnull))。请参阅Apple博客文章中的示例。







总结如下: / p>

如果您想标记您的方法EXPECT您的参数为非 - nil ,因此表明用 nil 调用它是一个逻辑错误,你应该执行以下操作:

   - (void)insertNameInDictionary:(NSString *)nameString __attribute __((nonnull))
{
// __attribute __((nonnull))允许检查明显的情况(直接传递nil)在编译时
NSParameterAssert(nameString); // NSParameterAssert允许在运行时检查其他情况(传递一个可能是或不是nil的变量)
[myDictionary setObject:nameString forKey:@Name];
}

如果你认为 nil 可以发生并且可以接受,你只是想在这些情况下避免崩溃,只需做这样的事情:

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

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

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

最后一种情况,如果你想传递 nil 在该特定示例中,只需 myDictionary 中移除 名称 ,你可以简单地做一个简单的 if test并调用 removeObjectForKey:@Name if nameString nil 并调用 setObject:forKey:如果不是......或者你可以使用KVC和泛型 setValue:forKey:(KVC方法,根本不特定于NSDictionary,所以不要与 setObject:forKey:)在NSDictionary的情况下,确实具有相同的行为(如果我们为值参数传递 nil ,则从字典中删除密钥)。


When coding a method or function, is a good practice to check the input arguments to respond to any possible fail scenario.

For example:

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

This looks ok, but if nameString is nil, the app will crash. So, we can check if it's nil, right?. We can also check if it is a NSString and not a NSNumber or if it is responds to the methods that our method needs to call.

So my question is: Which is the most complete and elegant way to check these arguments?

解决方案

There are multiple ways to implement such protection:

  • NSParameterAssert
  • __attribute__((nonnull))
  • and a simple if test.

[EDIT] Since the latest Xcode versions (Xcode 6), Apple added nullability annotations which is another — and better — way to express if a parameter can be nil / null or not. You should migrate to that notation instead of using __attribute__((nonnull)) to make your APIs even more readable.


In details:

  1. NSParameterAssert is Apple's dedicated macro that checks a condition on a method parameter and throws a dedicated exception if it fails.

    • This is a protection only at Runtime
    • This macro still considers that passing nil as a parameter is a programmation/conception error, considering that due to your application workflow it should normally never happen (due to other conditions ensuring the param never being nil for example), and if it's not something really went wrong.
    • It still thows an exception (that your calling code may @try/@catch if necessary), but it has the advantage that the exception thrown is more explicit (telling that the parameter was expected not to be nil instead of crashing with an ugly and hard-to-understand callstack/message).
  2. If you want to allow your code to call your method with nil but just do nothing in that case, you may simply if (!param) return at the beginning of the function/method.

    • This considers that passing nil is NOT a programmation/conception error and thus can sometimes happen due to the workflow of your application, so that's an acceptable case that can happen and just shouldn't crash.
  3. Less commonly known, there is the __attribute__((nonnull)) GCC/LLVM attribute that is dedicated to tell the compiler that some parameters of a function/method are expected to be not-null. This way, if the compiler can detect at compile time that you try to call your method/function with a nil/NULL argument (like directly calling insertNameInDitionary:nil instead of using a variable which value can't be determined at compile time yet), it will emit a compilation error right away to let you fix it ASAP.

  4. [EDIT] Since latest Xcode 6, you can (and should) use nullability annotations instead of __attribute__((nonnull)). See examples in Apple's Blog post.


So in summary:

If you want to mark that your method EXPECT your parameter to be non-nil, thus indicating that calling it with nil is a logic error, you should do the following:

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

If you think that calling your method with nil can happen and is acceptable, and you just want to avoid a crash in those cases, simply do stuff like this:

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

And if you think that you should be able to insert a nil object in your dictionary, you can convert the nil value to NSNull in that specific case so that it insert the NSNull singleton that is dedicated to such usage (I used the short form of the ?: ternary operator to make the code more compact):

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

And last case, if you want that passing nil in that particular example simply removes the Name from myDictionary, you can simply either do a simple if test and call removeObjectForKey:@"Name" if nameString is nil and call setObject:forKey: if it's not… or you could use KVC and the generic setValue:forKey: (KVC method, not specific to NSDictionary at all, so not to be confused with setObject:forKey:) that in the case of NSDictionary exactly has the same behavior (removing the key from the dictionary if we pass nil for the value parameter).

这篇关于在Objective-C中检查方法参数的最佳方法是哪种?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆