[Review] SE-0160: Limiting @objc inference


(Víctor Pimentel) #1

Sorry if this is too long, but this proposal would greatly impact
developers such as myself.

* What is your evaluation of the proposal?

Strongly against it.

I currently work in an iOS project with around 15 developers where the
main target (not counting extensions or frameworks) has +1600 ObjC
classes and +600 Swift files, and we are relying in the mix and match
for DI and testability reasons. Swift 3 migration was not easy, I tell
you :wink:

In practice for us this change will mean that we will need prefix
every method and property with @objc for most of our NSObject
subclasses. And if we left any of them out the compiler will not warn
us and it could end up in production crashes and, worse, undefined
behaviour. While we don't want to add such @objc annotation, we surely
don't want those crashes.

* Is the problem being addressed significant enough to warrant a change to Swift?

My understanding is that the main problem is: "[...] Swift's rules for
inference of @objc are fairly baroque, and it is often unclear to
users when @objc will be inferred.".

Yes, that problem is significant enough and in real world apps that
live in the ObjC runtime we are seeing that this slows down a bit the
development and crashes are likely to end up in production. We are
careful enough so that not we don't have so many crashes, but in
development we have many wtf moments where we revisit why our code
isn't working as expected. This is very difficult to explain to
newcomers.

But I'm just not seeing how this proposal would fix this problem, if
any, it would make it worse and introduce lots of more bugs.

In fact the proposal states that some of the detected problems "are
out-of-scope". I'm very concerned by that, I see that the proposal has
been carefully thought but that avoiding bugs/crashes in shipping apps
is not a top priority.

This post kind of summarizes the current state of method dispatch within Swift:

https://www.raizlabs.com/dev/2016/12/swift-method-dispatch/

If I were to create an updated table with the proposed changes, would
that table be more complicated or less complicated? From my point of
view, it would complicate things even more.

So for me the important thing to ask would be: How do we want NSObject
subclasses to look like so that they are safer, faster and more
expressive than now?

I understand that the Core team may not be aligned with this view
since you don't want to compromise speed in such objects, even though
for shipping apps the speed gained at this level is much less
appreciated that the safety. But for me a NSObject subclass should
work as close as possible as if it was in ObjC. So I agree with the
Brian King post in that NSObject subclasses should always be dynamic.
In fact:

- If you inherit from NSObject, I would expose every method/property
to the ObjC runtime.
- If you inherit from NSObject, any property/method that cannot be
exposed to the ObjC runtime should have a @nonobjc annotation. The
compiler could easily enforce this.
- I would remove the dynamic keyword, since that is not a pure Swift
construct (not forever, but at least until it becomes a pure Swift
construct).
- I would remove the need for @objc except in the case of a name
refinement and protocol declaration. This includes optional methods in
@objc protocols.
- (I'm less sure about this) Private methods in NSObject subclasses
written in Swift should act as ObjC non-visible methods, they should
be dynamic but they should not appear in the bridging header.

These changes do not affect pure Swift classes or structs, so the fact
that right now Cocoa and other frameworks force you to use NSObject
subclasses in specific situations do not prevent you from using joyful
pure-Swift collaborators. Since it will make everything much more
predictable, this change will probably bump the use of pure Swift
constructs, which will make those pieces of code much more portable
than semi-NSObject classes. Of course this is completely out of scope
for this review, but it shows that there is another way of making
things simpler for every developer that now needs to write NSObject
subclasses.

In the future and in my particular case, I'm dreaming of Apple
frameworks that do not rely in the ObjC runtime, and of Swift
improvements to introspection and other dynamic features. In that
dream, your classes should not inherit from NSObject if you don't
want, and any of the changes that the community makes to @objc
inference will not apply.

* Does this proposal fit well with the feel and direction of Swift?

As stated in https://swift.org/about/, the main goals of Swift are:

1. Safety, to the end that "developer mistakes should be caught before
software is in production".
2. Fastness, to the end that "Swift is intended as a replacement for
C-based languages (C, C++, and Objective-C). As such, Swift must be
comparable to those languages in performance for most tasks.
Performance must also be predictable and consistent [...]"
3. Expressiveness, "to offer syntax that is a joy to use".

One by one:

1. To me this proposal is against such safety, and it will clearly
introduce difficult-to-track bugs.
2. I understand that iOS/Mac applications that do not need such
dynamism will be faster with this change. But, for humans, I feel that
it will not be predictable and consistent. And since it looks as
arbitrary as the current solution, it will cause many headaches to
newcomers and experienced developers alike. And for me it's clear that
if you follow this path, new rules will be implemented in Swift 5 and
6, and most app developers will scratch their heads to remember what
are the actual rules for the current Swift version.
3. For expressiveness... well, I'm already inheriting from NSObject,
so what every Cocoa citizen expects is to be dynamic, so that we can
work with Interface Builder, target/selector APIs, KVC and so on. I
don't know any developer who wants to remember to put @objc in front
of every method and property that wants to live in the ObjC runtime.
That is just too ugly.

* If you have you used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No.

* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

In-depth study. Sorry for the lengthy email, but this has the
potential to negatively impact the life of every Swift iOS/Mac
developer that I know :slight_smile:

Thanks,

···

--

Víctor Pimentel