Skip to content

Commit af7f149

Browse files
author
Dave Abrahams
committed
[stdlib] Clean up/redocument custom bridging
1 parent bbbe605 commit af7f149

File tree

1 file changed

+145
-38
lines changed

1 file changed

+145
-38
lines changed

stdlib/public/core/BridgeObjectiveC.swift

Lines changed: 145 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#if _runtime(_ObjC)
14-
/// A Swift Array or Dictionary of types conforming to
15-
/// `_ObjectiveCBridgeable` can be passed to Objective-C as an NSArray or
16-
/// NSDictionary, respectively. The elements of the resulting NSArray
17-
/// or NSDictionary will be the result of calling `_bridgeToObjectiveC`
18-
/// on each element of the source container.
14+
/// A protocol for value types with custom bridging behavior.
1915
public protocol _ObjectiveCBridgeable {
16+
17+
/// An Objective-C type to which `Self` can be losslessly round-trip
18+
/// converted.
2019
associatedtype _ObjectiveCType : AnyObject
2120

2221
/// Convert `self` to Objective-C.
@@ -37,52 +36,160 @@ public protocol _ObjectiveCBridgeable {
3736
result: inout Self?
3837
)
3938

40-
/// Try to bridge from an Objective-C object of the bridged class
41-
/// type to a value of the Self type.
42-
///
43-
/// This conditional bridging operation is used for conditional
44-
/// downcasting (e.g., via as?) and therefore must perform a
45-
/// complete conversion to the value type; it cannot defer checking
46-
/// to a later time.
47-
///
48-
/// - parameter result: The location where the result is written.
49-
///
39+
/// Implement `x as? Self` where x is of type `_ObjectiveCType`.
40+
///
5041
/// - Returns: `true` if bridging succeeded, `false` otherwise. This redundant
51-
/// information is provided for the convenience of the runtime's `dynamic_cast`
52-
/// implementation, so that it need not look into the optional representation
53-
/// to determine success.
42+
/// information is provided for the convenience of the runtime's
43+
/// `dynamic_cast` implementation, so that it need not look into the
44+
/// optional representation to determine success.
45+
///
46+
/// - Parameter result: a container for the new instance of `Self`.
47+
/// - Postcondition: If ``true` is returned, `result` is non-`nil`.
5448
@discardableResult
5549
static func _conditionallyBridgeFromObjectiveC(
5650
_ source: _ObjectiveCType,
5751
result: inout Self?
5852
) -> Bool
5953

60-
/// Bridge from an Objective-C object of the bridged class type to a
61-
/// value of the Self type.
54+
/// Convert an instance of `_ObjectiveCType?` to an instance of `Self` at an
55+
/// imported Objective-C method boundary.
56+
///
57+
/// This operation is used where an Objective-C method signature claims the
58+
/// dynamic type of an instance is `_ObjectiveCType` with the correct
59+
/// substructure. For example:
60+
///
61+
/// @objc class ImportedClass {
62+
/// func foo() -> [String] // -(NSArray<NSString>* _NonNull)foo
63+
/// func bar(_ x: [String]) // -(void)bar:(NSArray<NSString>* _NonNull)
64+
/// }
6265
///
63-
/// This bridging operation is used for unconditional bridging when
64-
/// interoperating with Objective-C code, either in the body of an
65-
/// Objective-C thunk or when calling Objective-C code, and may
66-
/// defer complete checking until later. For example, when bridging
67-
/// from `NSArray` to `Array<Element>`, we can defer the checking
68-
/// for the individual elements of the array.
66+
/// The Objective-C signatures claim the `NSArray*`s returned from `foo` and
67+
/// passed to overrides of `bar` will only contain `NSString`s. We take that
68+
/// claim on faith and do not (necessarily) check its validity. If the
69+
/// imported signatures lie about parameter or return types, it may lead to
70+
/// undefined behavior.
6971
///
70-
/// \param source The Objective-C object from which we are
71-
/// bridging. This optional value will only be `nil` in cases where
72-
/// an Objective-C method has returned a `nil` despite being marked
73-
/// as `_Nonnull`/`nonnull`. In most such cases, bridging will
74-
/// generally force the value immediately. However, this gives
75-
/// bridging the flexibility to substitute a default value to cope
76-
/// with historical decisions, e.g., an existing Objective-C method
77-
/// that returns `nil` to for "empty result" rather than (say) an
78-
/// empty array. In such cases, when `nil` does occur, the
79-
/// implementation of `Swift.Array`'s conformance to
80-
/// `_ObjectiveCBridgeable` will produce an empty array rather than
81-
/// dynamically failing.
72+
/// The signatures also claim the `NSArray*`s will be non-null, but
73+
/// nullability is commonly mis-annotated in Objective-C. As a resilience
74+
/// measure against mis-annotation, this method accepts `source` as an
75+
/// `Optional`, so implementations can return an appropriate default value
76+
/// (e.g. an empty `Array`) when `source` is `nil`.
8277
static func _unconditionallyBridgeFromObjectiveC(_ source: _ObjectiveCType?)
8378
-> Self
8479
}
8580

81+
public protocol _BridgePolicy {
82+
static func bridge<Target>(
83+
_ source: AnyObject, to target: Target.Type) -> Target?
84+
}
85+
86+
struct _ForceCasting : _BridgePolicy {
87+
static func bridge<Target>(
88+
_ source: AnyObject, to target: Target.Type) -> Target? {
89+
return (source as! Target)
90+
}
91+
}
92+
93+
struct _ConditionalCasting : _BridgePolicy {
94+
static func bridge<Target>(
95+
_ source: AnyObject, to target: Target.Type) -> Target? {
96+
return source as? Target
97+
}
98+
}
99+
100+
public protocol _CustomObjectiveCBridgeable : _ObjectiveCBridgeable {
101+
static func _bridged<Policy: _BridgePolicy>(
102+
from objc: _ObjectiveCType, by policy: Policy.Type) -> Self?
103+
104+
static func _bridgedFromNil(_: _ObjectiveCType?) -> Self
105+
}
106+
107+
extension _ObjectiveCBridgeable {
108+
public static func _bridgedFromNil(_: _ObjectiveCType?) -> Self {
109+
fatalError(
110+
"Objective-C nil of type \(String(reflecting: _ObjectiveCType.self)) "
111+
+ "does not bridge to \(String(reflecting: Self.self))")
112+
}
113+
}
114+
115+
116+
extension _ObjectiveCBridgeable where Self : _CustomObjectiveCBridgeable {
117+
/// Bridge from an Objective-C object of the bridged class type to a
118+
/// value of the Self type.
119+
///
120+
/// This bridging operation is used for forced downcasting (e.g.,
121+
/// via as), and may defer complete checking until later. For
122+
/// example, when bridging from `NSArray` to `Array<Element>`, we can defer
123+
/// the checking for the individual elements of the array.
124+
///
125+
/// - parameter result: The location where the result is written. The optional
126+
/// will always contain a value.
127+
public static func _forceBridgeFromObjectiveC(
128+
_ source: _ObjectiveCType,
129+
result: inout Self?
130+
) {
131+
result = Self._bridged(
132+
from: source, by: _ForceCasting.self).unsafelyUnwrapped
133+
}
134+
135+
/// Implement `x as? Self` where x is of type `_ObjectiveCType`.
136+
///
137+
/// - Returns: `true` if bridging succeeded, `false` otherwise. This redundant
138+
/// information is provided for the convenience of the runtime's
139+
/// `dynamic_cast` implementation, so that it need not look into the
140+
/// optional representation to determine success.
141+
///
142+
/// - Parameter result: a container for the new instance of `Self`.
143+
/// - Postcondition: If ``true` is returned, `result` is non-`nil`.
144+
@discardableResult
145+
public static func _conditionallyBridgeFromObjectiveC(
146+
_ source: _ObjectiveCType,
147+
result: inout Self?
148+
) -> Bool {
149+
result = Self._bridged(from: source, by: _ConditionalCasting.self)
150+
return result != nil
151+
}
152+
153+
/// Convert an instance of `_ObjectiveCType?` to an instance of `Self` at an
154+
/// imported Objective-C method boundary.
155+
///
156+
/// This operation is used where an Objective-C method signature claims the
157+
/// dynamic type of an instance is `_ObjectiveCType` with the correct
158+
/// substructure. For example:
159+
///
160+
/// @objc class ImportedClass {
161+
/// func foo() -> [String] // -(NSArray<NSString>* _NonNull)foo
162+
/// func bar(_ x: [String]) // -(void)bar:(NSArray<NSString>* _NonNull)
163+
/// }
164+
///
165+
/// The Objective-C signatures claim the `NSArray*`s returned from `foo` and
166+
/// passed to overrides of `bar` will only contain `NSString`s. We take that
167+
/// claim on faith and do not (necessarily) check its validity. If the
168+
/// imported signatures lie about parameter or return types, it may lead to
169+
/// undefined behavior.
170+
///
171+
/// The signatures also claim the `NSArray*`s will be non-null, but
172+
/// nullability is commonly mis-annotated in Objective-C. As a resilience
173+
/// measure against mis-annotation, this method accepts `source` as an
174+
/// `Optional`, so implementations can return an appropriate default value
175+
/// (e.g. an empty `Array`) when `source` is `nil`.
176+
static func _unconditionallyBridgeFromObjectiveC(
177+
_ source: _ObjectiveCType?
178+
) -> Self {
179+
if _slowPath(source == nil) {
180+
return self._bridgedFromNil(source)
181+
}
182+
if let result = Self._bridged(from: source!, by: _ForceCasting.self) {
183+
return result
184+
}
185+
fatalError(
186+
"Bridging from Objective-C type "
187+
+ "\(String(reflecting: _ObjectiveCType.self)) to Swift type "
188+
+ "\(String(reflecting: Self.self)) failed with actual value "
189+
+ "\(String(reflecting: source))")
190+
}
191+
}
192+
86193
//===--- Bridging for metatypes -------------------------------------------===//
87194

88195
/// A stand-in for a value of metatype type.

0 commit comments

Comments
 (0)