> On Apr 4, 2016, at 9:22 PM, Brent Royal-Gordon via swift-evolution
> <[email protected]> wrote:
>
>>
>> https://github.com/apple/swift-evolution/blob/master/proposals/0058-objectivecbridgeable.md
>
> There are a number of things I'm not really clear on.
>
> * * *
>
> Consider this Objective-C API:
>
> ObjCFizzer* myFizzer;
>
> Which of these represents how it is imported?
>
> var myFizzer: ObjCFizzer
> var myFizzer: Fizzer
The latter. The idea is that the importer sees the bridged type is available
and substitutes it on all imported signatures. The actual mechanics of that
will involve some generated code (thunk) to call the protocol. I could update
the proposal to include what the body of that thunk might look like but it
didn’t seem terribly interesting.
>
> Suppose there is also a subclass (say, ObjCMutableFizzer), and we have this
> Objective-C API:
>
> ObjCMutableFizzer* mutableFizzer;
>
> Which of these represents how it is imported?
>
> var myMutableFizzer: ObjCMutableFizzer
> var myMutableFizzer: Fizzer
The intention there is that it imports as the bridged type so the latter.
>
> On the basis of NSArray and friends, I assume they come across like this:
>
> var myFizzer: Fizzer
> var myMutableFizzer: ObjCMutableFizzer
>
> Is that correct?
No
>
> * * *
>
> I assume that you can use casts to implicitly cross the bridge in both
> directions, even when Objective-C is not involved. That is, you could write
> something like this:
>
> ObjCFizzer() as Fizzer
>
> Is that correct?
>
> If you can cross the bridge purely in Swift, does the object type actually
> have to be @objc? Why?
>
> If it does not have to be @objc, is this perhaps better thought of as an
> `ObjectBridgeable` protocol which allows cast-based conversion of any type to
> an equivalent class, and which also does Objective-C bridging if the class
> happens to be @objc? (This looser definition might help us if we ever
> interoperate with other object systems on different platforms.)
In theory you could do this, though it would be a useless waste of CPU cycles :)
My thinking was that we may need similar bridging protocols in some
hypothetical world where we can import C++ objects but the shape of the
protocol (and certainly the constraints on the associated type) would be quite
different.
>
> * * *
>
> Suppose you have a value of type `ObjCMutableFizzer`:
>
> let mutableFizzer: ObjCMutableFizzer = ...
>
> Can you write `mutableFizzer as! Fizzer`? In other words, if a type is
> bridged to a particular class, is it also bridged to its subclasses?
>
> Based on the examples from Foundation, I suspect the answer is "yes”.
Yes, you can invoke as? or as! casts which will call the respective
initializers, though the default implementation of unconditional bridging (as!)
will call the conditional initializer.
>
> * * *
>
> Foundation classes can sometimes be bridged using an upcast (a plain `as`),
> which cannot crash. Is this possible with ObjectiveCBridgeable? If so, how?
> If not, will Foundation classes lose that ability?
>
> If this feature can't be expressed with ObjectiveCBridgeable, is this seen as
> a shortcoming we should try to overcome, or the proper design? I worry about
> the unnecessary proliferation of exclamation points, especially since many
> style guides strongly discourage them, which will turn this into an
> unnecessary proliferation of unnecessary `if let`s.
This would not be possible. This sort of bridging only works with special magic
types because they are known to always succeed. There is no condition under
which Swift will fail to convert String to NSString. The compiler/runtime can’t
prove that about any arbitrary type.
For bridging an Objective-C library into Swift, ideally all the APIs will be
annotated with SWIFT_BRIDGED so on import the Swift code won’t even be aware
the Objective-C type exists. All you’ll see in Swift is the appropriate Swift
types. This gives a library (say Photos.framework or UIKit) the chance to
provide truly native Swift types by shipping a module with combined Swift and
Objective-C code.
Similarly, going the other direction (an app with Objective-C and Swift code)
this proposal eliminates the need to deal with the Objective-C types in Swift.
The only situation where casting might be required is interop with things like
performSelector, context objects, or when SWIFT_BRIDGED annotations are missing.
>
> * * *
>
> I'm confused by a statement in the "Ambiguity and Casting" section:
>
> 2. A Swift type may bridge to an Objective-C base class then provide
> different subclass instances at runtime, but no other Swift type may bridge
> to that base class or any of its subclasses.
> i. The compiler should emit a diagnostic when it detects two
> Swift types attempting to bridge to the same ObjectiveCType.
>
> Does this mean that each bridged class must have exactly one corresponding
> Swift type? Or does it mean that if a bridged type has more than one
> corresponding Swift class, an explicit cast is always needed? Or merely that
> it may sometimes be needed?
As of right now, the proposal says that is an error to define in Swift code and
the compiler will generate an error.
>
> There are examples in the frameworks of many types bridging to a single
> class. For instance, a whole hoard of numeric types bridge to NSNumber; Swift
> handles this by exposing Swift types like `[Int]` as `NSArray<NSNumber*>*`,
> but doesn't do any converting when going in the opposite direction. Is that
> how this new protocol works, or does it do something else?
I’ve gone back and forth on the ambiguity section (In one version of the
proposal, ambiguity just disabled automatic thunk generation and required you
to perform manual casting). NSNumber brings up an interesting case. Several
Swift types should adopt NSNumber as their bridging target, but wouldn’t be
able to under the proposed rule. However allowing this for the general case may
be problematic. There is already special compiler magic for importing things
like NSInteger and Int bridges to NSInteger or NSNumber depending on whether it
is inside a collection. I’m curious what other people’s thoughts are on this.
>
> * * *
>
> I'm confused by the SWIFT_BRIDGED() macro. Why does it exist? Doesn't the
> ObjectiveCBridgeable conformance provide all the information needed? What
> happens if you don't include it? (Perhaps you get the one-way bridging
> behavior seen with `NSNumber`?)
For an Objective-C framework, the definitions of the @objc types will be in
Objective-C headers, not an auto-generated Swift bridging header. The macro is
both for the auto-generated header but also for library writers to use in their
Objective-C headers.
>
> * * *
>
> The "Resilience" section says:
>
> Adding or removing conformance to ObjectiveCBridgeable, or changing the
> ObjectiveCType is a fragile (breaking) change.
>
> Why is this? In particular, why is adding a conformance a breaking change?
> That isn't the normal rule for protocols.
Because it changes the shape of the API of a client importing it.
For similar reasons requiring the bridging to be in same-named modules is to
prevent the shape of an imported API changing because you imported a separate
module that added some bridging conformance.
>
> * * *
>
> Probably a stupid question, but I want to be certain since the example does
> something else: There would not be some kind of circularity problem with
> defining `ObjCFizzer` in terms of `Fizzer`, would there? For instance:
>
> class ObjCFizzer: NSObject {
> fileprivate var fizzer: Fizzer
>
> fileprivate init(_ fizzer: Fizzer) {
> self.fizzer = fizzer
> }
>
> var fizzyString: String? {
> guard case .case1(let string) = fizzer else { return
> nil }
>
> return string
> }
>
> var fizzyX: Int? {
> guard case .case2(let x, _) = fizzer else { return nil }
> return x
> }
>
> var fizzyY: Int? {
> guard case .case2(_, let y) = fizzer else { return nil }
> return y
> }
> }
> extension ObjCFizzer {
> convenience init(string: String) {
> self.init(.case1(string))
> }
>
> convenience init(x: Int, y: Int) {
> fizzer = .case2(x, y)
> }
> }
>
> I have a place in my code where I would like to use ObjectiveCBridgeable, but
> it uses a pattern like this, so I'd like to make sure I'll be able to adopt
> the feature.
>
You can certainly “wrap” the underlying Swift type and expose accessors for it
as you’ve done here. There are no issues with circularity at all.
I realize that the proposal doesn’t explicitly state it but generally a type
that is @objc can’t conform to ObjectiveCBridgeable. (Even if it did, the
conformance would be useless.)
> * * *
>
> In general, I feel like this proposal is written in a way which is accessible
> to the people who participated in the original discussion, but not to others.
>
> This is a proposal which contains seven uses of the word "thunk" and zero
> uses (as opposed to conformances) of the proposed protocol in examples. It
> seems to imply that existing bridging of Foundation types might change, but
> does not explain what those changes will mean for user code. It is
> fundamentally an Objective-C bridging feature, but it contains no examples of
> Objective-C.
The intent is for existing foundation types to continue bridging just as they
do today with no changes.
Russ
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution