A dynamic foundation
Brent Simmons, in a series of posts, argued the case for making Swift more dynamic like Objective-C.
In one of those posts, he summarizes his case as:
I’m documenting problems that Mac and iOS developers solve using the dynamic features of the Objective-C runtime. […]
The point is that these problems will need solving in a possible future world without the Objective-C runtime. These kinds of problems should be considered as that world is being designed. The answers don’t have to be the same answers as Objective-C – but they need to be good answers, better than (for instance) writing a script to generate some code.
Though we app developers rarely have to deal directly with the Objective-C runtime, many frameworks we use do that in order to give us powerful features in deceptively simple interfaces (like KVO and Core Data). That, in turn, makes it easier for us to develop apps.
So the reduced dynamism in Swift doesn’t affect us now, but it might hit us many years (or decades) later, when eventually, Swift completely supersedes Objective-C and Apple starts making Swift frameworks that are meant to be used only from Swift, and therefore don’t use the Objective-C runtime at all.
But I’m not too worried about it because I think Swift has a good foundation to build its dynamism on. I share Brent Simmons’ optimism in his earlier post:
I strongly suspect that Swift is designed with dynamic programming in mind — and is potentially better for dynamic programming than Objective-C. (Since, after all, it drops the C, is a fresh start that learns from the past, doesn’t have to worry about breakage so much — and, hey, look at that team.)
If this is true — and I don’t have an easy way to evaluate it, so I’ll assume it — then we can assume that the reason it hasn’t been an issue so far is that we already have the Objective-C runtime. (Even if all your code is Swift, your app is using that runtime.)
I believe there’s a way to evaluate how true Brent’s optimistic suspicion is: To look at Swift’s internals at present and see if the dynamism we desire can be built on top of it. (Update: This does not constitute evidence. See updates below.)
Internally, Swift class instances, even of pure Swift classes 1, have
a memory layout that looks exactly like an Objective-C class instance -
with an isa
pointer that points to a class object with a
dispatch table (as Mike Ash found out early on). However,
while in Objective-C, all methods are listed in the dispatch table, in
Swift, only those methods marked dynamic
are listed 2. Since Swift
guarantees that dynamic
methods are never devirtualized, and since
dynamic
is part of Swift’s library ABI goals, it’s
highly likely that the dispatch table would remain in future Swift versions
as well (See update below).
The presence of this dispatch table would enable Objective-C-like
dynamic features to be built on top. I’m reasonably hopeful that well
before it’s time for Objective-C to be phased out, Swift would have
gained equivalent dynamic features, though maybe not with the same
interface. However, unlike Objective-C, the dynamism in Swift would
almost definitely remain disabled by default and would need to be
enabled for specific methods and properties using the dynamic
modifier.
Update:
Jordan Rose from the Swift team:
Without commenting on anything else, I wouldn’t count this as evidence; it’s not present on Linux.
dynamic
is currently limited to@objc
methods, which is fine because there’s no other way to replace them.
This is indeed true. Even the doc for dynamic
starts with “When Swift APIs are imported by the Objective-C runtime”. My mistake in having missed noting that when I wrote this post.
Since the dynamic
modifier exists only for interoperability with
Objective-C, this isn’t sufficient evidence to say that Swift classes
will gain dynamism similar to Objective-C classes.
A related fun observation: All pure Swift root classes have an isa
saying that they inherit from a SwiftObject
class, which is at present
actually written in Objective-C 3, conforming to the
NSObject
protocol (and the code is designed to work even when
Swift-Objective-C-interoperability is not required i.e. when
(Update: SWIFT_OBJC_INTEROP
is falseSwiftObject
is not even available
when Swift-Objective-C-interoperability is off).
Because of this, you can actually send respondsToSelector:
to an
instance of a pure Swift class to check for methods marked dynamic
(after casting to AnyObject
to keep the compiler happy):
class C {
func staticfn() { }
dynamic func dynamicfn() { }
}
let c = C()
(c as! AnyObject).respondsToSelector("nonexistingfn") // false
(c as! AnyObject).respondsToSelector("staticfn") // false
(c as! AnyObject).respondsToSelector("dynamicfn") // true
Update:
A good amount of
SwiftObject
comes down to “there should be no problems putting a Swift object in an NSArray”.
That makes sense as well. Swift classes pose as Objective-C classes when interoperating with Objective-C so that the Objective-C code can talk to Swift without having to resort to Swift-specific hacks.
Given how pure Swift classes closely resemble Objective-C classes internally, I think Swift will eventually gain dynamic features comparable to Objective-C, and I think that will happen well before it’s time for the Objective-C runtime to get sunsetted.
Update:
All of this doesn’t mean we won’t do it! It just means we won’t do it just because Objective-C does it.
So there’s still hope. :)
-
What I call pure Swift classes are classes that are neither marked
@objc
, nor haveNSObject
as an ancestor ↩ -
Because it’s written in Objective-C, the runtime dynamism probably doesn’t work on Linux (I don’t know for sure because I haven’t yet tried it on Linux), but that’s not a problem for me as an app developer ↩
This post was discussed in Swift Weekly.