blob: 4e121be6e01ba94d70094a55eb59a06b14c6dbe0 [file] [log] [blame]
:orphan:
============================
Objective-C Interoperability
============================
This document tracks the differences between the Swift and Objective-C ABIs and
class models, and what it would take to merge the two as much as possible. The
format of each section lays out the differences between Swift and Objective-C,
then describes what needs to happen for a user to mix the two seamlessly.
.. warning:: This document was used in planning Swift 1.0; it has not been kept
up to date and does not describe the current or planned behavior of Swift.
.. contents::
Terminology used in this document:
- ``id``-compatible: something that can be assigned to an ``id`` variable and
sent messages using ``objc_msgSend``. In practice, this probably means
implementing the ``NSObject`` protocol, since most of Cocoa doesn't check
whether something implements ``NSObject`` before sending a message like
``-class``.
- Objective-C isa: something that identifies the class of an Objective-C object,
used by ``objc_msgSend``. To say a Swift object has an Objective-C isa does
*not* mean that a fully-formed Objective-C runtime class structure is
generated for the Swift class; it just means that (1) the header of the Swift
object "looks like" an Objective-C object, and (2) the parts of an Objective-C
class used by the ``objc_msgSend`` "fast path" are the same.
Design
======
All Swift objects [#]_ will be ``id``-compatible and will have an Objective-C
isa, on the assumption that you want to be able to put them in an array, set
them as represented objects, etc. [#]_
Swift classes that inherit from NSObject (directly or indirectly [#]_) behave
exactly like Objective-C classes from the perspective of Objective-C source.
All methods marked as "API" in Swift will have dual entry points exposed by
default. Methods not marked as "API" will not be exposed to Objective-C at all.
Instances of these classes can be used like any other Objective-C objects.
Subclassing a "Swift NSObject class" in Objective-C requires a bit of extra
work: generating Swift vtables. We haven't decided how to do this:
- Clang could be taught about Swift class layout.
- The Clang driver could call out to the Swift compiler to do this. Somehow.
- The runtime could fill in the vtable from the Objective-C isa list at class
load time. (This could be necessary anyway to support dynamic subclassing...
which we may or may not do.)
Swift classes that do not inherit from NSObject are not visible from
Objective-C. Their instances can be manipulated as ``id``, or via whatever
protocols they may implement.
::
class AppController : NSApplicationDelegate {
func applicationDidFinishLaunching(notification : NSNotification) {
// do stuff
}
}
// Use 'id <NSApplicationDelegate>' in Objective-C.
Like "Swift NSObject classes", though, "pure" Swift classes will still have an
isa, and any methods declared in an Objective-C protocol will be emitted with
dual entry points.
.. [#] Really, "All Swift objects on OS X and iOS". Presumably a Swift compiler
on another system wouldn't bother to emit the Objective-C isa info.
.. [#] Dave is working out an object and class layout scheme that will minimize
the performance cost of emitting both the Objective-C isa and a Swift vtable.
It is entirely possible that from the Swift perspective, the Objective-C isa
is just an opaque "vtable slice" that is fixed at offset 0.
.. [#] ...or any other Objective-C class, including alternate roots like
NSProxy. Most likely this will be implemented with an inherited attribute
``[objc]`` on the class, which would even allow Swift to create Objective-C
root classes.
Use Cases
=========
*Unfinished and undetailed.*
Simple Application Writer
-------------------------
I want to write my new iOS application in Swift, using all the Objective-C
frameworks that come with iOS.
Guidelines:
Everything should Just Work™. There should be no need to subclass NSObject
anywhere in your program, unless you are specifically specializing a class in
the Cocoa Touch frameworks.
Intermediate Application Writer
-------------------------------
I want to write my new application in Objective-C, but there's a really nice
Swift framework I want to use.
Guidelines:
- Not all Swift methods in the framework may be available in Objective-C. You
can work around this by adding *extensions* to the Swift framework classes to
expose a more Objective-C-friendly interface. You will need to mark these new
methods as "API" in order to make them visible to Objective-C.
- "Pure" Swift classes will not be visible to Objective-C at all. You will have
to write a wrapper class (or wrapper functions) in Swift if you want to use
the features of these classes directly. However, you can still treat them
like any other objects in your program (store them in ``id`` variables,
Objective-C collections, etc).
Transitioning Application Writer
--------------------------------
I have an existing Objective-C application, and I want to convert it
piece-by-piece to Swift.
Guidelines:
- Swift is different from Objective-C in that methods in Swift classes are not
automatically usable from everywhere. If your Swift class inherits from
NSObject, marking your methods as "API" will allow them to be called from
Objective-C code. A Swift class that does not inherit from NSObject will only
respond to messages included in its adopted protocols. [#]_
- Once you have finished transitioning to Swift, go through your classes and
remove the "API" marker from any methods that do not need to be accessed from
Objective-C. Remove NSObject as a superclass from any classes that do not need
to be accessed from Objective-C. Both of these allow the compiler to be more
aggressive in optimizing your program, potentially making it both smaller and
faster.
.. [#] If you explicitly want to expose a Swift method to Objective-C, but it
is not part of an existing protocol, you can mark the method as "API" and
include the ``[objc]`` attribute::
// Note: This syntax is not final!
func [API, objc] accessibilityDescription {
return "\(self.givenName) \(self.familyName)"
}
New Framework Writer
--------------------
I want to write a framework that can be used by anyone.
Requirements:
- Can call (at least some) Swift methods from Objective-C.
Intermediate Framework Writer
-----------------------------
I have an existing Objective-C framework that I want to move to Swift.
Requirements:
- Can subclass Objective-C classes in Swift.
- Can call (at least some) Swift methods from Objective-C.
Decisions:
- Should I expose Swift entry points as API?
- If so, should they be essentially the same as the Objective-C entry points, or
should I have a very different interface that's more suited for Swift (and
easily could be "better")?
End User
--------
- Things should be fast.
- Things should not take a ton of memory.
Nice to Have (uncategorized)
----------------------------
- Can write a Swift extension for an Objective-C class.
- Can write a Swift extension for an Objective-C class that adopts an
Objective-C protocol.
- Can write a Swift extension for an Objective-C class that exposes arbitrary
new methods in Objective-C.
Tradeoffs
=========
This section discusses models for various runtime data structures, and the
tradeoffs for making Swift's models different from Objective-C.
Messaging Model
---------------
Everything is ``id``-compatible:
- Less to think about, maximum compatibility.
- Every Swift object must have an Objective-C isa.
Non-NSObjects are messageable but not ``id``-compatible:
- Cannot assign Swift objects to ``id`` variables.
- Cannot put arbitrary Swift objects in NSArrays.
- Potentially confusing: "I can message it but I can't put it in an ``id``??"
- Clang must be taught how to message Swift objects and manage their retain
counts.
- On the plus side, then non-NSObjects can use Swift calling conventions.
- Requires framework authors to make an arbitrary decision that may not be
ABI-future-proof.
Non-NSObjects are opaque:
- Can be passed around, but not manipulated.
- ...but Clang probably *still* has to be taught how to manage the retain count
of an opaque Swift object, and doing so in the same way as dispatch_queue_t
and friends may be dangerous (see <os/object.h> -- it's pretending they're
NSObjects, which they are)
- Requires framework authors to make an arbitrary decision that may not be
ABI-future-proof.
Method Model
------------
*This only affects methods marked as "API" in some way. Assume for now that all
methods use types shared by both Objective-C and Swift, and that calls within
the module can still be optimized away. Therefore, this discussion only applies
to frameworks, and specifically the use of Swift methods from outside of the
module in which they are defined.*
Every method marked as API can *only* be accessed via Objective-C entry points:
- Less to think about, maximum compatibility.
- Penalizes future Swift clients (and potentially Objective-C clients?).
Every method marked as API can be accessed both from Objective-C and Swift:
- Maximum potential performance.
- Increases binary size and linking time.
- If this is a framework converted to Swift, clients that link against the
Swift entry points are no longer backwards-compatible. And it's hard to know
what you did wrong here.
- Overriding the method in Objective-C requires teaching Clang to emit a Swift
vtable for the subclass.
Methods marked as "ObjC API" can only be accessed via Objective-C entry points;
methods marked as "Swift API" can only be accessed via Swift entry points:
- Changing the API mode breaks binary compatibility.
- Obviously this attribute is inherited -- overriding an Objective-C method
should produce a new Objective-C entry point. What is the default for new
methods, though? Always Swift? Always Objective-C? Based on the class model
(see below)? Specified manually?
Methods marked as "ObjC API" can be accessed both from Objective-C and Swift;
methods marked as "Swift API" can only be accessed via Swift entry points:
- More potential performance for the shared API.
- Increases binary size and linking time.
- Overriding the method in Objective-C requires teaching Clang to emit a Swift
vtable for the subclass.
- Same default behavior problem as above -- it becomes a decision.
Class Model
-----------
All Swift classes are layout-compatible with Objective-C classes:
- Necessary for ``id``-compatibility.
- Increases binary size.
Only Swift classes marked as "ObjC" (or descending from an Objective-C class)
are layout-compatible with Objective-C classes; other classes are not:
- Requires framework authors to make an arbitrary decision.
- Changing the API mode *may* break binary compatibility (consider a Swift
subclass that is not generating Objective-C class information).
Subclassing Model
-----------------
*Requirement: can subclass Objective-C objects from Swift.*
All Swift classes can be subclassed from Objective-C:
- Potentially increases binary size.
- Requires teaching Clang to emit Swift vtables.
Only Swift classes marked as "ObjC" (or descending from an Objective-C class)
are subclassable in Objective-C:
- Probably *still* requires teaching Clang to emit Swift vtables.
- Requires framework authors to make an arbitrary decision that may not be
ABI-future-proof.
Method Overriding Model
-----------------------
*Requirement: Swift classes can override any Objective-C methods.*
Methods marked as "overridable API" only have Objective-C entry points:
- Less to think about, maximum compatibility.
- Penalizes future Swift clients (and potentially Objective-C clients?).
Methods marked as "overridable API" have both Objective-C and Swift entry
points:
- Requires teaching Clang to emit Swift vtables.
- Increases binary size and link time.
Methods marked as "overridable API" have only Swift entry points:
- Requires teaching Clang to emit Swift vtables.
- Later exposing this method to Objective-C in a subclass may be awkward?
Attributes for Objective-C Support
==================================
``@objc``
- When applied to classes, directs the compiler to emit Objective-C metadata
for this class. Additionally, if no superclass is specified, the superclass
is implicitly ``NSObject`` rather than the default ``swift.Object``.
Note that Objective-C class names must be unique across the entire program,
not just within a single namespace or module. [#]_
- When applied to methods, directs the compiler to emit an Objective-C entry
point and entry in the Objective-C method list for this method.
- When applied to properties, directs the compiler to emit Objective-C methods
``-``\ *foo* and ``-set``\ *Foo*\ ``:``, which wrap the getter and setter
for the property.
- When applied to protocols, directs the compiler to emit Objective-C metadata
for this protocol. Objective-C protocols may contain optional methods.
Method definitions for an Objective-C protocol conformance are themselves
implicitly ``@objc``.
This attribute is inherited (in all contexts).
``@nonobjc``
- When applied to methods, properties, subscripts or constructors, override the
implicit inheritance of ``@objc``.
- Only valid if the declaration was implicitly ``@objc`` as a result of the
class or one of the class's superclasses being ``@obj`` -- not permitted on
protocol conformances.
- It is permitted to override a ``@nonobjc`` method with a method marked as
``@objc``; overriding an ``@objc`` (or implicitly ``@objc``) method with a
``@nonobjc`` method is not allowed.
- It is an error to combine ``@nonobjc`` with ``dynamic``, ``@IBOutlet`` or
``@NSManaged``.
This attribute is inherited.
``@IBOutlet``
Can only be applied to properties. This marks the property as being exposed
as an outlet in Interface Builder. **In most cases,**
`outlets should be weak properties`__.
*The simplest implementation of this is to have* ``@IBOutlet`` *cause an*
*Objective-C getter and setter to be emitted, but this is* not *part of*
``@IBOutlet``'s *contract.*
This attribute is inherited.
``@IBAction``
Can only be applied to methods, which must have a signature matching the
requirements for target/action methods on the current platform.
This marks the method as being a potential action in Interface Builder.
*The simplest implementation of this is to have* ``@IBAction`` *imply*
``@objc``, *and this may be the* only *viable implementation given how the*
*responder chain works. For example, a window's delegate is part of the*
*responder chain, even though it does not subclass* ``NSResponder`` *and may*
*not be an Objective-C class at all. Still, this is* not *part of*
``@IBAction``'s *contract.*
This attribute is inherited.
.. [#] I'm not really sure what to do about uniquing Objective-C class names.
Maybe eventually [objc] will take an optional argument specifying the
Objective-C-equivalent name.
__ https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW6
Level 1: Message-passing
========================
*Assuming an object is known to be a Swift object or an Objective-C object at
compile-time, what does it take to send a message from one to the other?*
ARC
---
By default, objects are passed to and returned from Objective-C methods as +0
(i.e. non-owned objects). The caller does not have to do anything to release
returned objects, though if they wish to retain them they may be able to steal
them out of the top autorelease pool. (In practice, the caller *does* retain
the arguments for the duration of the method anyway, unless it can be proven
that nothing interferes with the lifetime of the object between the load and
the call.)
Objective-C methods from certain method families do return +1 objects, as do
methods explicitly annotated with the ``ns_returns_retained`` attribute.
All Swift class objects (i.e. as opposed to structs) are returned as +1 (i.e.
owned objects). The caller is responsible for releasing them.
Swift methods that are exposed as Objective-C methods will have a wrapper
function (thunk) that is responsible for retaining all (object) arguments and
autoreleasing the return value.
*Swift methods will **not** be exposed as* ``ns_returns_retained`` because they
should behave like Objective-C methods when called through an* ``id``.
Arguments
---------
Objective-C currently requires that the first argument be ``self`` and the
second be ``_cmd``. The explicit arguments to a method come after ``_cmd``.
Swift only requires that the first argument be ``self``. The explicit
arguments come after ``self``.
The thunk mentioned above can shift all arguments over...which doesn't really
cost anything extra since we already have to retain all the arguments.
Output Parameters
-----------------
Because Objective-C does not have tuples, returning multiple values is
accomplished through the use of pointer-to-object-pointer parameters, such as
``NSError **``. Additionally, objects returned through these parameters are
conventionally autoreleased, though ARC allows this to be specified
explicitly.
Swift has tuples and does not have pointers, so the natural way to return
multiple values is to return a tuple. The retain-count issue is different
here: with ARC, the tuple owns the objects in it, and the caller owns the
tuple.
Swift currently also has ``[inout]`` arguments. Whether or not these will be
exposed to users and/or used for Objective-C out parameters is still
undecided.
*This issue has not been resolved, but it only affects certain API.*
Messaging ``nil``
-----------------
In Objective-C, the result of messaging ``nil`` is defined to be a zero-filled
value of the return type. For methods that return an object, the return value
is also ``nil``. Methods that return non-POD C++ objects attempt to
default-construct the object if the receiver is ``nil``.
In Swift, messaging ``nil`` is undefined, and hoped to be defined away by the
type system through liberal use of some ``Optional`` type.
- I've seen other languages explicitly request the Objective-C behavior using
``foo.?bar()``, though that's not the prettiest syntax in the world.
-Jordan
As long as the implementation of ``Optional`` is layout-compatible with an
object pointer, and an absent ``Optional`` is represented with a null pointer,
this will Just Work™.
Overloading
-----------
In Objective-C, methods cannot be overloaded.
In Swift, methods can have the exact same name but take arguments of different
types.
Note that in Swift, all parameters after the first are part of the method
name, unless using the "selector syntax" for defining methods::
// 1. foo:baz:
func foo(Int bar, Int baz);
// 2. foo:qux:
func foo(Int bar, Int qux);
// 3. foo:qux: (same as above)
func foo(Int bar) qux(Int quux);
// 4. foo:baz: (but different type!)
func foo(Int bar, UnicodeScalar baz);
a.foo(1, 2) // ambiguous in Swift (#1 or #2?)
a.foo(1, baz=2) // calls #1
a.foo(1, qux=2) // calls #2/3 (the same method)
a.foo(1, 'C') // calls #4, not ambiguous in Swift!
[a foo:1 baz:2]; // ambiguous in Objective-C (#1 or #4?)
[a foo:1 qux:2]; // calls #2/3 (the same method)
The Swift compiler should not let both #1 and #4 be exported to Objective-C.
It should already warn about the ambiguity between #1 and #2 without using
named parameters.
Level 2: Messaging ``id``
=========================
*If a Swift object can be referenced with* ``id``, *how do you send messages to*
*it?*
Note: the answer might be "Swift objects can't generally be referenced with
``id``".
``isa`` Pointers
----------------
The first word of every Objective-C object is a pointer to its class.
We might want to use a more compact representation for Swift objects...
...but we can't; see below.
Method Lookup
-------------
Objective-C performs method lookup by searching a sequence of maps for a
given key, called a *selector*. Selectors are pointer-sized and uniqued
across an entire process, so dynamically-loaded methods with the same name as
an existing method will have an identical selector. Each map in the sequence
refers to the set of methods added by a category (or the original class). If
the lookup fails, the search is repeated for the superclass.
Swift performs method lookup by vtable. In order to make these vtables
non-fragile, the offset into a vtable for a given message is stored as a
global variable. Rather than chaining searches through different message
lists to account for inheritance and categories, the container for each
method is known at compile-time. So the final lookup for a given method looks
something like this::
vtable[SUBCLASS_OFFSET + METHOD_OFFSET]
Swift class objects will have ``isa`` pointers, and those ``isa`` pointers will
have an Objective-C method list at the very least, and probably a method cache
as well. The methods in this list will refer to the Objective-C-compatible
wrappers around Swift methods described above.
The other words in the ``isa`` structure may not be used in the same way as they
are in Objective-C; only ``objc_msgSend`` has to avoid special-casing Swift
objects. Most of the other runtime functions can probably do a check to see if
they are dealing with a Swift class, and if so fail nicely.
Level 3a: Adopting Objective-C Protocols in Swift
=================================================
- Bare minimum for implementing an AppKit/UIKit app in Swift.
- Essentially the same as emitting any other Objective-C methods, plus making
``-conformsToProtocol:`` and ``+conformsToProtocol:`` work properly.
Level 3b: Adopting Swift Protocols in Objective-C
=================================================
- Requires generating both Swift and Objective-C entry points from Clang.
- Requires generating Swift protocol vtables.
*Note: including protocol implementations is essentially the same as implicitly
adding an extension (section 5a).*
Level 4a: Subclassing Objective-C Classes in Swift
==================================================
*To be written.*
- Basically necessary for implementing an AppKit/UIKit app in Swift.
- Requires generating Objective-C-compatible method lists.
- When a new method is marked as API, does it automatically get the Objective-C
calling conventions by default? (See "Tradeoffs" section.)
Level 4b: Subclassing Swift Classes in Objective-C
==================================================
*To be written.*
- May require generating Swift vtables.
Alternative: if a method is exposed for overriding, it only gets an
Objective-C entry point. (Downsides: performance, other platforms will hate
us.)
Alternative: only Swift classes with an Objective-C class in their hierarchy
can be subclassed in Objective-C. Any overridden methods must be exposed as
Objective-C already. (Downsides: framework authors could forget to inherit
from NSObject, Swift code is penalized ahead of time.)
Alternative: only Swift classes with an Objective-C class in their hierarchy
are *visible* in Objective-C. All other Swift objects are opaque.
(Downsides: same as above.)
Level 5a: Adding Extensions to Objective-C Classes in Swift
===========================================================
*To be written.*
- May require generating Objective-C-compatible method lists.
- Less clear what the *default* calling convention should be for new methods.
Level 5b: Adding Categories to Swift Classes in Objective-C
===========================================================
*To be written.*
- Does not actually *require* generating Swift vtables. But we could if we
wanted to expose Swift entry points for these methods as well.
- Does require an Objective-C-compatible ``isa`` to attach the new method list
to.
Level 6: Dynamic Subclassing
============================
*To be written, but probably not an issue...it's mostly the same as statically
subclassing, right?*
Level 7: Method Swizzling
=========================
I'm okay with just saying "no" to this one.