Brent Simmons:
And then it gets just a little bit worse still: Mail on iOS 8 does have the ability to keep swiping to get the default action. Mail must be doing this as a custom thing or using private APIs to get this.
Which means that if we adopted the standard behavior, people would ask us why we don’t “just” do what Mail does.
Which argues for not touching this at all, since we already do a custom thing which works.
But that, again, means we’ve got this custom code to maintain and a bit of UI that’s quite unlike what users expect. (Which isn’t necessarily the worst thing. There can be good reasons for doing something different.)
Design iOS iOS 8 iOS App Vesper
I highly recommend the Edge Cases episode Swift Is a Really Good Thing and a Step Back. There’s much to like about Swift, but much has been lost as well: instantiating classes by name, the ability to write your own Core Data–like framework, message passing (contra vtable dispatch), swizzling to work around bugs and to add features, proxies/bridges, and more.
The quote in this post’s title, from Andrew Pontious, refers to the general lack of outrage over the loss of dynamism. In broad strokes, the C++ people have asserted their vision that the future will be static, and the reaction from the Objective-C crowd has been apathy. Apple hasn’t even really tried to make a case for why this U-turn is a good idea, and yet the majority seems to have rolled over and accepted it, anyway.
Since the episode was released, there’s been some good news: Wolf Rentzsch is not leaving Edge Cases, as was implied. More mixed is the news of the significant changes that Apple has made to Swift in beta 5.
The latest version of The Swift Programming Language discusses the new dynamic
modifier:
Apply this modifier to any member of a class that can be represented by Objective-C. When you mark a member declaration with the dynamic
modifier, access to that member is always dynamically dispatched using the Objective-C runtime. Access to that member is never inlined or devirtualized by the compiler.
Because declarations marked with the dynamic
modifier are dispatched using the Objective-C runtime, they’re implicitly marked with the objc
attribute.
Previously, there was only the @objc
attribute, and this made it seem like message passing was deprecated. The only way to get it was via the Objective-C compatibility layer, and the @
sign emphasized that it was not part of the language proper.
One way to interpret the addition of the dynamic
modifier, which lacks a @
, is that Apple plans for message passing to be at least an optional part of Swift going forward. It’s currently implemented using the Objective-C runtime, but it’s no longer tied to @objc
. In a future release, there could be pure Swift classes that use message passing.
However, the other half of this change is that methods in Objective-C classes are no longer dynamic by default. The dynamic
modifier doesn’t actually make things more dynamic; rather, Apple has made everything more static but allowed you to opt out by annotating individual methods. To define an Objective-C method, you used to type just -
. Now you must type func dynamic
. This reminds me of how you have to type nonatomic
on most Objective-C properties because the default is usually not what you want.
The latest version of Using Swift with Cocoa and Objective-C says:
While the @objc
attribute exposes your Swift API to the Objective-C runtime, it does not guarantee dynamic dispatch of a property, method, subscript, or initializer. The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime. When you mark a member declaration with the dynamic
modifier, access to that member is always dynamically dispatched. Because declarations marked with the dynamic
modifier are dispatched using the Objective-C runtime, they’re implicitly marked with the @objc
attribute.
Requiring dynamic dispatch is rarely necessary. However, you must use the dynamic
modifier when you know that the implementation of an API is replaced at runtime. For example, you can use the method_exchangeImplementations
function in the Objective-C runtime to swap out the implementation of a method while an app is running. If the Swift compiler inlined the implementation of the method or devirtualized access to it, the new implementation would not be used.
The good news is that Apple has found a way to speed up Objective-C classes in Swift. Presumably, this only applies to methods that are compiled into the same binary as their callers. My guess is that calls into the Cocoa frameworks cannot be optimized in this way.
The bad news is that, by default, your objects will no longer work with KVO or swizzling. If the designer of a class doesn’t anticipate a reason for a particular method or property to be dynamic, that lack of foresight gets baked in at compile time.
In my view, this is a mistake. It makes sense for dynamism to be the default because the costs are low. Objective-C message dispatch is very fast—famously running on 68030 hardware and of course the original iPhone. And, secondly, the Pareto principle applies: when message passing is a bottleneck, most of the gains can be obtained by making just a few hot spots more static (e.g. using IMP
-caching).
The costs for not using message passing, on the other hand, can be high because they make the code more rigid/brittle. You cannot retroactively make compiled code more dynamic/flexible. And yet, since dynamic
is not the default, the odds are that a lot more methods will be static than need to be. Most of the time, objc_msgSend
is not why your code is slow, yet Swift acts like it needs to protect you from this.
The common case is that message sending is fast enough and that programmers are not smart enough to predict all the ways their code will be used in the future. So Apple should optimize for that and make dynamic
the default.
Language Design Message Passing Objective-C Objective-C Runtime Optimization Swift Programming Language