Swift 4: Bridging Peephole for “as” Casts
John McCall (via Peter Steinberger):
Bridging conversions are not always desirable. First, they do impose some performance overhead which the user may not want. But they can also change semantics in unwanted ways. For example, in certain rare situations, the reference identity of an
NSString
return value is important — maybe it's actually a persistentNSMutableString
which should be modified in-place, or maybe it's a subclass which carries additional information. A pair of bridging conversions fromNSString
toString
and then back toNSString
is likely to lose this reference identity. In the current representation,String
can store anNSString
reference, and if theString
is bridged toNSString
that reference will be used as the result; however, the bridging conversion fromNSString
does not directly store the originalNSString
in theString
, but instead stores the result of invoking+copy
on it, in an effort to protect against the originalNSString
being somehow mutable.Bridging conversions arising from reasons #1 and #2 are avoidable, but bridging conversions arising from reason #3 currently cannot be eliminated without major inconvenience, such as writing a stub in Objective-C. This is unsatisfactory. At the same time, it is not valid for Swift to simply eliminate pairs of bridging conversions as a matter of course, precisely because those bridging conversions can be semantically important. We do not want optimization settings to be able to affect things as important as whether a particular
NSString
is mutable or not.
He proposes eliminating pairs of bridging conversions under certain circumstances:
This would avoid the bridging conversions through
[View]
on the return value of the getter:let subviews = view.subviews as NSArrayThis would not:
let subviews = view.subviews let nsSubviews = subviews as NSArrayThis would avoid the bridging conversion through
[CIFilter]
on the argument to the setter:view.backgroundFilters = nsFilters as [CIFilter]This would not:
let filters = nsFilters as [CIFilter] view.backgroundFilters = filters
6 Comments RSS · Twitter
I don't like that proposal. It's way too subtle and easy to miss if there's not a comment going with it that explains the hidden behavior.
I am also surprised that an automatic type conversion would already happen during the "let x = view.subviews". I don't think that's how Swift works internally, or does it? I mean, no language should behave like that. Only when an assignment encounters an explicit type on the left side, the type conversion rules should apply, making his two alternatives behave the same, because after "let subviews = view.subviews" the subviews's type would still be the one returned by view.subviews, i.e. it would remain a NSArray, or Swift already create its own array container (using +copy) at that point? If that were indeed the case, then I think it would be smarter to introduce a new "sticky" attribute for types that says "avoid auto-conversion into Swift types", e.g. by writing "let subviews = view.subviews.keepOriginalType", so that now subviews has a type that says "I'm an ObjC type that shall not be auto-converted". Though, I realize, I am now mixing static (compile-time) and runtime behavior here.
Oh well, I guess this is the wrong place to discuss this anyway. I just hope that his proposal won't get accepted, although I agree with the need to do something about the problem he explains.
Oh wait - that's an Apple engineer?! So he has the power to implement this? And there has been no concerns raised? Oh, damn.
@Thomas He’s one of the key compiler guys. Super smart.
As I understand it, the root issue is that the type returned by view.subviews
is not NSArray
because Swift changes it to a Swift array when it imports the headers. If it didn't do that, you’d have to manually cast/convert at nearly every use. Auto-converting is a reasonable solution to that problem but needs an escape hatch for when performance or semantics really matter. I agree that the proposal is subtle. I like the idea but would prefer something more explicit.
[…] Swift.Codable, Swift 4: Key-Value Observation, Swift’s Error Handling Implementation, Swift 4: Bridging Peephole for “as” Casts, Swift 4: Synthesizing Equatable and Hashable Conformance, Swift 4: JSON With Encoder and […]
[…] method, which has a different signature and so bridges differently. But it seems to also work to disable the bridging by casting with as […]