objective-c – Mark方法永不返回nil

我正在使用Objective-C开发一个API,这个API有一些带有一些虚构方法的协议:

- (NSString *)gimmeString; // Want implementations to never return nil

我非常喜欢提供上下文,所以我大量使用了所有内容,包括__attribute __((nonnull))和朋友等属性.我问的是,有没有办法提供上下文,并可能添加编译时检查方法实现说“使用clang编译时此方法永远不会返回nil”?

即我喜欢这样的东西:

@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

我理解在编译时不可能完全确定,但检测返回nil;或确定评估为零的功能是好的.

像__attribute __((objc_method_family(copy)))之类的想法看起来很怪异和不可接受,但我没有找到更好的东西,只是添加评论让我的API用户处于一个更可怕,更不可靠的世界.

最佳答案 – XCode 6.3 –

从XCode 6.3开始,你实际上可以annotate pointers to be nullable and nonnull并获得编译时检查.

- (nonnull NSString *)gimmeString;

– Pre XCode 6.3 –

经过一些研究,包括挖掘clang和gcc文档,我发现在Objective-C中没有办法实现我想要的东西.

想法为什么会这样:

我相信这是因为在编译时无法确定某些复杂的质量水平.您无法确定方法是否始终在编译时返回not-nil.并且确定方法是否可以返回nil是不稳定的,猜测,可以评估所有

return %something_which_evaluates_to_nil_at_compile_time%;

并警告所有依赖于它的方法,但是,例如,你不能确定某些-init方法并不总是返回nil或某些web请求得到你的请求而没有在任何地方提供的附加上下文并且你最终会假阴性.

甚至-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扩展).

点赞