MANIAK_dobrii
MANIAK_dobrii

Reputation: 6032

Mark method to never return nil

I'm developing an API using Objective-C, this API has protocol with some fictional method:

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

I'm a big fan of providing context, so I heavily use everything for that purpose including attributes like __attribute__((nonnull)) and friends. What I'm asking is if there a way to provide context and possibly add a compile time check for method implementation saying "this method never returns nil" when compiled with clang?

i.e. I'd love to have something like:

@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

instead of just:

@protocol MyProtocol
// This method should never return nil
- (NSString *)gimmeString;
@end 

@implementation MyProtocolAdopter
- (NSString *)gimmeString
{
    // muvahaha, I lied!
    return nil;
}
@end

I understand it is impossible to fully determine that at compile time, but detecting return nil; or functions which for sure evaluate to nil is fine.

Idea with something like __attribute__((objc_method_family(copy))) seems weird and unacceptable, but I didn't manage to find anything better then just adding a comment leaving my API users in a bit scarier and more unreliable world.

Upvotes: 4

Views: 397

Answers (1)

MANIAK_dobrii
MANIAK_dobrii

Reputation: 6032

- XCode 6.3 -

Since XCode 6.3 you, actually, can annotate pointers to be nullable and nonnull and get compile time checks.

- (nonnull NSString *)gimmeString;


- Pre XCode 6.3 -

After some research, which included digging into clang and gcc docs I found that there's no way to achieve what I want in Objective-C.

Thoughts why is that so:

I believe that's because there's no way to determine that at compile time at some sophisticated level of quality. You can't tell for sure if method always returns not-nil at compile time. And determining if method could return nil is something shaky, guess, it's possible to evaluate all

return %something_which_evaluates_to_nil_at_compile_time%;

and warn on all methods which depend on that, but, for example, you can't be sure that some -init method does not always return nil or some web request get's you a request without additional context provided everywhere and you'll end up with false-negatives.

Even -dequeueReusableCellWithIdentifier:forIndexPath: from UITableView which guaranties to always return valid cell is defined as just:

- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
                           forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);


What I've came up with:

So, what I've came up in Objective-C is providing context without compile-time checks. I add comments like that:

@protocol MyProtocol
/**
 *  Creates and returns a string.
 *
 *  @note Implementations should never return nil.
 *
 *  @return A string, guarantied not to be nil.
 */
- (NSString *)gimmeString;
@end

Also one more thing could be done to make this more readable, we can define an empty define and append it to the end of declaration, like this:

// 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

So eye catches this line making your code more readable, making your code users happier because they understand what you want to emphasise easier.

Swift

And the last but not the least is suggestion to move to Swift. In Swift this is possible and built in, you just define your method to return not an optional type:

protocol MyProtocol {
    func gimmeString -> String
}

And you're good to go.

Conclusion

If you really want checks like this in Objective-C there's no magic, you only do it at the run time, at the same time you could provide context, which is great. But oh, wait, there's also Swift option, we'll anyway migrate to it at some time, so take your time and spend some of it learning this great new language. This need is a great example where Swift's safety features are really suitable.

UPD: Played with OCLint a bit, looks like something like this could be achieved using it (or just writing a custom Clang extension).

Upvotes: 3

Related Questions