[swift-evolution] [Pitch] Changing NSObject dispatch behavior

Kevin Ballard kevin at sb.org
Wed Dec 14 20:32:33 CST 2016

On Wed, Dec 14, 2016, at 05:54 PM, Brian King wrote:
> > Please no. Just because I have to subclass NSObject doesn't mean I want to discard the performance benefits of static dispatch, and it especially doesn't mean I want to actually change the semantics of method resolution. Taking an existing Swift class and changing its base class to NSObject should not change how its methods are dispatched.
> Subclassing NSObject already changes how dispatch happens. NSObject
> extensions will use message dispatch for instance. I really don't
> think that table -> message dispatch will result in a real life
> performance impact, but I agree that consistency is valuable.
> The static dispatch upgrade loss is disappointing. In practice
> however, I don't think that this has ever had an impact on my code. If
> performance is a consideration, most people just drop NSObject. If you
> are using NSObject, you are probably using it because of a large
> objective-c code base, in which case, I don't think the profiler is
> really going to notice the few statically dispatched functions.

Obj-C compatibility certainly is one reason to use NSObject, but perhaps a bigger reason is because you're subclassing some framework-provided class, like UIView or UIViewController. Just because I'm subclassing UIViewController doesn't mean I want my controller's API to be subject to Obj-C method dispatch unnecessarily.

And even in the cases where I'm subclassing NSObject for Obj-C compatibility, that doesn't mean that most of the calls to my object are coming from Obj-C. All that means is that at least one place my object is used is Obj-C, and 99% of the uses might be Swift. Or if I'm writing a library, it might mean that I simply want to preserve Obj-C compatibility in case a client wants it, even though I expect my clients to be primarily Swift (For example, this is how postmates/PMHTTP is written; Obj-C–compatible, but the expectation is most clients will probably be Swift).

> > Interaction with Obj-C runtime machinery stuff like KVO should be opt-in. In Obj-C it's ad-hoc, many classes support it for properties but many also don't, and very few properties have their KVO conformance documented. I don't view having to mark my properties as `dynamic` to participate in KVO to be a problem with Swift but rather a feature. It tells the reader that this property supports KVO.
> This is an interesting point, and it would be an interesting semantic.
> However in practice, the majority of NSObject code is imported from
> obj-c, and it's generated interface does not follow this convention.

Just because framework classes don't follow this convention doesn't mean it's not a valuable convention to have in code written in Swift. There's a lot of things that Swift does better than Obj-C, and that you don't get when using an API imported from Obj-C, but that's not a reason to not do those things.

> Also, it's strange to conflate how something was dispatched and if it
> supported KVO. I think property delegates will be the future here and
> enable some exciting contracts if adopted.
> Another approach is to just make it easier to opt into the full obj-c
> machinery. The addition of a class or extension level `dynamic`
> keyword would be great. If we could get buy in on this, then the
> debate becomes if NSObject should default to `dynamic` or not.

I don't think we should ever make it possible to mark an entire class as `dynamic`. This just reintroduces the Obj-C problem, where many properties support KVO, but not all, and there's no indication on the property itself as to whether it supports it.

-Kevin Ballard

> Brian
> >
> > -Kevin Ballard
> >
> > On Wed, Dec 14, 2016, at 03:15 PM, Brian King via swift-evolution wrote:
> >> I wanted to follow up to a blog post I wrote about Message Dispatch in
> >> Swift — https://www.raizlabs.com/dev/2016/12/swift-method-dispatch. I
> >> mentioned some changes to NSObject that didn’t result in any
> >> objections, so I thought it was time to see what the SE mailing list
> >> thought.
> >>
> >> I’ve read a few conversations on SE mailing list that have morphed
> >> into abstract conversations about dynamic vs static dispatch. I want
> >> to focus specifically on how Swift NSObject subclasses behave.
> >>
> >> I think that there are 2 changes that will result in fewer bugs and
> >> will not have a substantial impact on performance:
> >>
> >>
> >> ## Remove Table Dispatch from NSObject
> >>
> >> NSObject subclasses use table dispatch for the initial class
> >> declaration block. I think that using message dispatch for NSObject
> >> subclasses everywhere will result in a much more consistent developer
> >> experience.
> >>
> >> ## Block NSObject Visibility Optimizations
> >>
> >> Swift upgrades method dispatch to final when the compiler can prove
> >> that the method is not subclassed. I would like to see Swift be more
> >> careful about the impact of these optimizations on message dispatch,
> >> and consider message dispatch non-upgradable.
> >>
> >>
> >> I thought it would help to frame this choice as a trade-off between
> >> Swift’s goals of safe, fast, and expressive.
> >>
> >> ## Safe
> >>
> >> Always using message dispatch for NSObject subclasses will fix a class
> >> of runtime errors in framework features that are designed around
> >> message passing (e.g. KVO). Arguments against using dynamic features
> >> like this are valid, but Cocoa frameworks still use dynamic features
> >> and the above behaviors result in actual bugs. As a bonus, this will
> >> resolve SR-584, where a table-dispatched method that is overridden by
> >> a message dispatch method doesn’t behave correctly.
> >>
> >> ## Fast
> >>
> >> The above changes will result in slower dispatch in NSObject
> >> subclasses. However, I don't think that these dispatch changes
> >> actually have a tangible impact on performance. Most NSObject
> >> subclasses sit on top of a lot of `objc_msgSend`, and if there is a
> >> specific hot spot, it would still be optimizable via the final
> >> keyword.
> >>
> >> ## Expressive
> >>
> >> Using table dispatch for NSObject without any source indication or
> >> library documentation is not very expressive. I think it’s important
> >> to weigh table dispatch behavior against all of the framework
> >> documentation and developer experience that assume message dispatch.
> >> This will also eliminate the need for a lot of `@objc` and `dynamic`
> >> annotations that are often inconsistently applied depending on if they
> >> are needed in the scope they are defined in (e.g. class vs extension).
> >>
> >>
> >> If this idea shows promise, I’d be glad to formalize a Swift Evolution
> >> Proposal and explore syntactic details. I think being able to flag a
> >> class with `dynamic` and applying this flag to `NSObject` may be the
> >> only syntactic change needed. However, it would be good to debate the
> >> merit of the behavior change before the syntax.
> >>
> >>
> >> Thanks!
> >>
> >>
> >> Brian King
> >> _______________________________________________
> >> swift-evolution mailing list
> >> swift-evolution at swift.org
> >> https://lists.swift.org/mailman/listinfo/swift-evolution
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution at swift.org
> > https://lists.swift.org/mailman/listinfo/swift-evolution

More information about the swift-evolution mailing list