Skip to content

Commit cc668c2

Browse files
authored
Merge pull request #25336 from mikeash/objc-interop-docs
2 parents 3754f4b + e39a235 commit cc668c2

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

docs/ObjCInterop.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Objective-C and Swift Interop
2+
3+
This document describes how Swift interoperates with Objective-C code and the
4+
Objective-C runtime.
5+
6+
Interactions between the Swift runtime and the Objective-C runtime are
7+
implementation details that are subject to change! Don't write code outside the
8+
Swift runtime that relies on these details.
9+
10+
This document only applies on platforms where Objective-C interop is available.
11+
On other platforms, Swift omits everything related to Objective-C.
12+
13+
## Messaging
14+
15+
Swift generates calls to `objc_msgSend` and variants to send an Objective-C
16+
message, just like an Objective-C compiler does. Swift methods marked or
17+
inferred as `@objc` are exposed as entries in the Objective-C method list for
18+
the class.
19+
20+
## Classes
21+
22+
All Swift classes are also Objective-C classes. When Swift classes inherit from
23+
an Objective-C class, they inherit in exactly the same way an Objective-C class
24+
would, by generating an Objective-C class structure whose `superclass` field
25+
points to the Objective-C superclass. Pure Swift classes with no superclass
26+
generate an Objective-C class that subclasses the internal `SwiftObject` class
27+
in the runtime. `SwiftObject` implements a minimal interface allowing these
28+
objects to be passed around through Objective-C code with correct memory
29+
management and basic functionality.
30+
31+
### Compiler-Generated Classes
32+
33+
Swift classes can be generated as part of the binary's static data, like a
34+
normal Objective-C class would be. The compiler lays down a structure matching
35+
the Objective-C class structure, followed by additional Swift-specific fields.
36+
37+
### Dynamically-Generated Classes
38+
39+
Some Swift classes (for example, generic classes) must be generated at runtime.
40+
For these classes, the Swift runtime allocates space using `MetadataAllocator`,
41+
fills it out with the appropriate class structures, and then registers the new
42+
class with the Objective-C runtime by using the SPI `objc_readClassPair`.
43+
44+
### Stub Classes
45+
46+
Note: stub classes are only supported on macOS 10.15+, iOS/tvOS 13+, and watchOS
47+
6+. The Objective-C runtime on older OSes does not have the necessary calls to
48+
support them. The Swift compiler will only emit stub classes when targeting an
49+
OS version that supports them.
50+
51+
Stub classes can be generated for dynamically-generated classes that are known
52+
at compile time but whose size can't be known until runtime. This unknown size
53+
means that they can't be laid down statically by the compiler, since the
54+
compiler wouldn't know how much space to reserve. Instead, the compiler
55+
generates a *stub class*. A stub class consists of:
56+
57+
uintptr_t dummy;
58+
uintptr_t one;
59+
SwiftMetadataInitializer initializer;
60+
61+
The `dummy` field exists to placate the linker. The symbol for the stub class
62+
points at the `one` field, and uses the `.alt_entry` directive to indicate that
63+
it is associated with the `dummy` field.
64+
65+
The `one` field exists where a normal class's `isa` field would be. The
66+
Objective-C runtime uses this field to distinguish between stub classes and
67+
normal classes. Values between 1 and 15, inclusive, indicate a stub class.
68+
Values other than 1 are currently reserved.
69+
70+
An Objective-C compiler can refer to these classes from Objective-C code. A
71+
reference to a stub class must go through a `classref`, which is a pointer to a
72+
class pointer. The low bit of the class pointer in the `classref` indicates
73+
whether it needs to be initialized. When the low bit is set, the Objective-C
74+
runtime treats it as a pointer to a stub class and uses the initializer function
75+
to retrieve the real class. When the low bit is clear, the Objective-C runtime
76+
treats it as a pointer to a real class and does nothing.
77+
78+
The compiler must then access the class by generating a call to
79+
the Objective-C runtime function `objc_loadClassref` which returns the
80+
initialized and relocated class. For example, this code:
81+
82+
[SwiftStubClass class]
83+
84+
Generates something like this code:
85+
86+
static Class *SwiftStubClassRef =
87+
(uintptr_t *)&_OBJC_CLASS_$_SwiftStubClassRef + 1;
88+
Class SwiftStubClass = objc_loadClassref(&SwiftStubClassRef);
89+
objc_msgSend(SwiftStubClass, @selector(class));
90+
91+
The initializer function is responsible for setting up the new class and
92+
returning it to the Objective-C runtime. It must be idempotent: the Objective-C
93+
runtime takes no precautions to avoid calling the initializer multiple times in
94+
a multithreaded environment, and expects the initializer function itself to take
95+
care of any such needs.
96+
97+
The initializer function must register the newly created class by calling the
98+
`_objc_realizeClassFromSwift` SPI in the Objective-C runtime. It must pass the
99+
new class and the stub class. The Objective-C runtime uses this information to
100+
set up a mapping from the stub class to the real class. This mapping allows
101+
Objective-C categories on stub classes to work: when a stub class is realized
102+
from Swift, any categories associated with the stub class are added to the
103+
corresponding real class.
104+
105+
## To Document
106+
107+
This document is incomplete. It should be expanded to include:
108+
109+
- Information about the ABI of ObjC and Swift class structures.
110+
- The is-Swift bit.
111+
- Legacy versus stable ABI and is-Swift bit rewriting.
112+
- Objective-C runtime hooks used by the Swift runtime.
113+
- And more?

0 commit comments

Comments
 (0)