Conflicting documentation for `[UIView initWithFrame:]`: nullable or nonnull?

468 views Asked by At

Using CLANG_ANALYZER_NONNULL (i.e. -Xclang nullability), I got "Null is returned from a function that is expected to return a non-null value":

enter image description here

Using Xcode 7.3 and iOS 9.3 documentation, I checked initWithFrame: and it can return nil:

description

But UIView.h encapsulates everything with NS_ASSUME_NONNULL_BEGIN, so we can interpret the following:

interface

as:

- (nonnull instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;

So documentation explains it's nullable, while header file says it's nonnull. Which one to trust?

Should I write:

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (!self) {
        // workaround for clang analyzer
        return (void * _Nonnull)nil;
    }
    // Initialization code
    return self;
}

Or:

- (nonnull instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    // Initialization code
    return self;
}

UPDATE

Xcode documentation was updated and is now: initWithFrame: documentation

So no more conflict.

2

There are 2 answers

3
Devin Coughlin On BEST ANSWER

In the case of UIView's -initWithFrame initializer, the recommendation is to not defensively check the result of calling the super initializer because there is realistically nothing an application can do to recover from a failed UIView allocation.

Also, as of Xcode 7.3 beta 4, the static analyzer no longer warns here. It now doesn't warn about returning nil from the -init, -copy, and -mutableCopy families even when these methods have a return type with a nonnull type qualifier to avoid warning on exactly this common defensive idiom.

0
Cristik On

The headers usually take precedence, as many times the documentation is either left behind or overlooked. UIKit (among the other CocoaTouch frameworks) headers are more up-to-date than the documentation for another reason: better Swift interoperability.

So you should go with the second approach:

- (nonnull instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    // Initialization code
    return self;
}

Returning nonnull from this initializer is also logical, as an UIView is an abstract object that holds information about something that will eventually be rendered on the screen, it doesn't have many restrictions on it own.

Similarly, it doesn't make much sense to pass a nil string to [NSURL URLWithString:] as NSURL strings have clearly defined requirements, and a nil one doesn't satisfy them, so it makes sense to have the nonnull annotation here.