我正在使用Objective-C开发API,此API具有一些虚构方法的协议:
- (NSString *)gimmeString; // Want implementations to never return nil
我非常喜欢提供上下文,所以我大量使用了所有内容,包括__attribute__((nonnull))
和朋友等属性。我要问的是是否有提供上下文的方法和可能为方法实现添加编译时检查说“此方法永远不会返回nil”用clang编译时?
即。我喜欢这样的东西:
@protocol MyProtocol
- (NSString *)gimmeString __attribute__((no_I_never_really_really_return_that_weird_nil));
@end
@implementation MyProtocolAdopter
- (NSString *)gimmeString
{
return nil; // WARNING! You're returning nil, YOU PROMISED!
}
@end
而不只是:
@protocol MyProtocol
// This method should never return nil
- (NSString *)gimmeString;
@end
@implementation MyProtocolAdopter
- (NSString *)gimmeString
{
// muvahaha, I lied!
return nil;
}
@end
我理解在编译时无法完全确定,但检测return nil;
或确定评估为nil
的函数是正常的。
像__attribute__((objc_method_family(copy)))
之类的想法似乎很奇怪且不可接受,但我没有找到更好的东西,只是添加评论让我的API用户处于一个更可怕,更不可靠的世界。
答案 0 :(得分:2)
因为XCode 6.3实际上可以annotate pointers to be nullable
and nonnull
并且可以进行编译时检查。
- (nonnull NSString *)gimmeString;
经过一些研究,包括深入研究clang和gcc文档,我发现在Objective-C中没有办法达到我想要的目标。
我相信这是因为没有办法在编译时确定处于某种复杂的质量水平。您无法确定方法是否始终在编译时返回非零值。并确定方法could
是否返回nil是不稳定的,猜测,它可以评估所有
return %something_which_evaluates_to_nil_at_compile_time%;
并警告依赖于此的所有方法,但是,例如,您无法确定某些-init
方法并不总是返回nil或某些网络请求让您获得请求没有在任何地方提供的附加上下文,你最终会得到假阴性。
-dequeueReusableCellWithIdentifier:forIndexPath:
来自UITableView
的保证始终返回有效单元格的定义只是:
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);
所以,我在Objective-C中提出的是提供没有编译时检查的上下文。我添加这样的评论:
@protocol MyProtocol
/**
* Creates and returns a string.
*
* @note Implementations should never return nil.
*
* @return A string, guarantied not to be nil.
*/
- (NSString *)gimmeString;
@end
还可以做一件事以使其更具可读性,我们可以定义一个空的定义并将其附加到声明的末尾,如下所示:
// Marks method to never return nil
#define MD_RETURNS_NONNULL
@protocol MyProtocol
/**
* Creates and returns a string.
*
* @note Implementations should never return nil.
*
* @return A string, guarantied not to be nil.
*/
- (NSString *)gimmeString MD_RETURNS_NONNULL;
@end
因此,眼睛抓住了这一行,使您的代码更具可读性,使您的代码用户更快乐,因为他们更容易理解您想要强调的内容。
最后但并非最不重要的是建议搬到斯威夫特。在Swift中,这是可能的并且是内置的,您只需定义返回不是可选类型的方法:
protocol MyProtocol {
func gimmeString -> String
}
你很高兴。
如果你真的想要在Objective-C中进行这样的检查,那么你只能在运行时进行检查,同时你可以提供上下文,这很好。但是,哦,等等,还有Swift选项,我们无论如何都要迁移到它,所以花点时间花一些时间学习这种伟大的新语言。这种需求是一个很好的例子,Swift的安全功能非常适合。
UPD:稍微玩OCLint,看起来像这样可以用它来实现(或者只是编写一个自定义的Clang扩展名)。