Re: Nullability annotation "best practice"
Re: Nullability annotation "best practice"
- Subject: Re: Nullability annotation "best practice"
- From: Quincey Morris <email@hidden>
- Date: Sun, 16 Aug 2015 17:47:26 +0000
On Aug 16, 2015, at 09:10 , Seth Willits <email@hidden> wrote:
>
> Really? This list has no opinions? That's hard to imagine :-)
Well, I do, but I didn’t post it because I didn’t think you’d like it.
I don’t think it’s worth annotating private methods at all (in general — I’m sure there are specific cases where it’s a clear benefit). It seems to me there are 2 main reasons why you’d want to:
1. Documentation/API contract
When creating methods for *others* to use, such as at the API boundary of a framework, the more clarity the better, and the more the compiler can check the better. But for methods that are internal to an implementation, the chances are that the “client" code will have been written before the nullability is settled — or the nullability is obvious enough that the annotation isn’t needed.
2. Swift compatibility
This is the important case, because the nullability changes the type of the interface when bridged into Swift.
When neither of the above is a controlling consideration, for the majority of pure Obj-C code, nullability annotations seem rather redundant. I see 3 issues:
a. Origination
For a method that originates an object pointer — typically an ‘init’ method or a factory class method that basically wraps an ‘init’ — I think it’s better not to return nil at all, in most cases. That is, if super init may itself return nil, then you should test for that and crash, rather than just passing on the nil.
If the originating method does need to indicate failure by returning nil, the pattern I’ve adopted is *always* to return a NSError via a NSError** parameter in the normal way. Not only is this self-annotating, both in the interface and at every call site, it also promotes proper error handling by making it clear when you’ve left an error unhandled at the call site.
b. Propagation
Once you’ve adopted a consistent origination pattern, then propagating object pointers (that is, passing them around as parameters) stops being problematic, mostly, since they mostly aren’t nil by accident …
c. Optionality
… except in the the case where you specify nil literally, as in the case of an optional parameter value. Some such cases are already covered by Obj-C coding patterns. For example, if you’re passing a dictionary of options, passing nil instead of an empty dictionary is probably all the same to the called code.
Sometimes it does matter. For example, when passing a completion block, it really matters whether it can be nil or not, because invoking a nil block pointer will crash. In such cases, the implementation (having decided whether it will check for nil or require non-nil) *should*, I think, annotate the method parameter, but I’d be inclined to do that in *both* the @implementation and @interface if both exist, just like you would for something like ‘const’.
My argument in all this is that Obj-C has its own characteristic patterns for handling nil object pointers, which may be diverse and situational, but have been honed by time. The new annotations (including NS_DESIGNATED_INITIALIZER and generics) seem to me to be geared towards reducing the impedance mismatch with Swift, and I think it distorts Obj-C to Swift-ize it if it’s not actually being bridged.
In the relatively rare cases where annotations resolve an existing pain point (such as nil vs. non-nil completion block pointers), then use them. But I’m unconvinced about the need for wholesale adoption.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden