Avoiding unwanted nullability warnings with Xcode 8?

Just updated to Xcode 8 b5 and am getting some understandable but annoying warnings for methods that call [NSObject init] but that are themselves marked as returning nonnull objects:


e.g


+ (nonnull ValidatedUserInterfaceItem*)validatedItemWithAction:(SEL)action tag:(NSInteger)tag
{
    ValidatedUserInterfaceItem* result = [[ValidatedUserInterfaceItem alloc] init];
    if (result)
    {
        result->_action = action;
        result->_tag = tag;
    }
    return result;
}


"Warning: Null is returned from a method that is expected to return a non-null value"


In this case it is because [NSObject init] has an implicit nullable result. But in real life if it ever were really nil then the app already in a bad state is not going to live very long anyway.


I don't want to have to specify that the method can return nullable as that then has ripple down effects to the callers too.


Since I have a dozen or so similar warnings throughout the code is there an easy way to avoid this warning while still specifying a nonnull result?

It's not [NSObject init], it's [ValidatedUserInterfaceItem init]. If ValidatedUserInterfaceItem is inheriting its init method from NSObject, the above piece of code doesn't know that, because Obj-C is a dynamic language.


In this situation, you should not use a branching control flow where both branches return. Instead, if 'result' is nil, you should crash the app immediately.


However, a better approach is to implement 'init' in ValidatedUserInterfaceItem, mark it nonnull, and crash there if the superclass init returns nil. Code like the above that creates ValidatedUserInterfaceItem instances should then simply omit the 'if' test.

Thanks for the response.


What I didn't realise is that without the 'if (result)' test the analyzer keeps quiet. Whilst that is 'nice' in this case, is it treating this specially compared to testing the value? i.e by the fact that I am testing result is the analyzer guessing that I think the result could indeed be nil when it otherwise would think it can't be???


i.e consider this similar example on a category of NSString:

- (nonnull NSString*)camelCaseString
{
    NSMutableString* result = [self mutableCopy];
  
        // Some processing here ...
  
    return result;
}


This compiles without warning even though mutableCopy 'could' return nil but likewise creates a warning with an 'if (result)' in the "Some Processing Here" section.


OK, so I could go around removing all the various seemingly redundant non-nil checks from my code, but they have been there long before nullability specifiers existed and I really don't want to have to go through them all unless there is no other way for the long term.


Coming at this from another angle. Does the analyzer have some smarts in it to assume that new, init, copy, mutableCopy etc are probably not going to be returning nil even though the headers imply that they can and thereby avaoid warnings about such uses?

>> by the fact that I am testing result is the analyzer guessing that I think the result could indeed be nil when it otherwise would think it can't be?


The analyzer does reason like this.


>> Does the analyzer have some smarts in it to assume that new, init, copy, mutableCopy etc are probably not going to be returning nil even though the headers imply that they can and thereby avaoid warnings about such uses?


It's unlikely that it reasons about "probably". It's more likely to be designed to make assumptions that produce the least number of false warnings, across some large body of sample code, and it may take into account which method or method family is involved, and whether there's a code path that acknowledges the possible nil value.


>> I really don't want to have to go through them all unless there is no other way


The point is that there's a genuine bug in this piece of code, at least in a local sense. If you want to fix such bugs, at least you have the compiler (now) telling you where they are.


Personally, I'd fix the problem. Actually, what I'd do (and have done) is go through every init in my code and remove the possibility of it returning nil, except when I really want it to be failable, in which case I added an "outError" parameter to provide an error if it returns nil. (Since I was doing this before nullability annotations were introduced, the extra parameter served to indicate that the init could fail. Whether you do that or not, you should now at least mark the un-failable ones as nonnull.) Then I'd remove all the redundant error testing and handling, and feel like I'd done a good day's work. 🙂


In this particular case, I'd also add an override of init to ValidatedUserInterfaceItem that simply invokes super and tests the result, in order to "erase" the inherited nullability, and declare the init in its header file so this is known to clients of the class.


Of course, you're certainly free to file a bug complaining about the change in compiler behavior. It's possible that the warning wasn't supposed to be added in this scenario.


Also, you're free to suppress the warning if you wish. Look in the build transcript and the error message should show the option for the warning (of the form "-fXXX", and you can specify the opposite ("-fnoXXX") for the project, for specific files or for specific sections of code within the file.

Avoiding unwanted nullability warnings with Xcode 8?
 
 
Q