1

I've experimented with casting many types to AnyObject. Reference types obviously cast unaltered, but others are automagically converted under the hood to NSNumber, NSValue, and so on. (These are not the exact types, but close enough.) I was even able to cast a tuple successfully.

This surprised me. Are there any types that cannot be cast to AnyObject?

There are other questions and answers that may cover this material, but none of them asks this question specifically and the answer to this question can only be found in the fine print of the other answers.

Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
  • Possible duplicate of [AnyObject and Any in Swift](https://stackoverflow.com/questions/25809168/anyobject-and-any-in-swift) – Tamás Sengel Nov 23 '17 at 18:28
  • I believe the answer is no, because `func x(value: T) -> AnyObject { return value as AnyObject }` always compiles without a forced cast. – Gregory Higley Nov 23 '17 at 18:29
  • On Apple platforms, *anything* can bridge to `AnyObject`, compare https://stackoverflow.com/q/39033194/2976878. – Hamish Nov 23 '17 at 18:30
  • @the4kman: Yeah … not really. While the answer to this question might be found in that one, the other is not specific enough and someone looking for an answer to this one might not realize it could be found there. – Gregory Higley Nov 23 '17 at 18:30
  • It depends what you mean by "cast". You can always do it, but you might not get something that Objective-C can use, because pure Swift types are just boxed opaquely. – matt Nov 23 '17 at 18:31
  • I'm not really worried about Obj-C. This came about as a result of https://stackoverflow.com/q/47441073/27779 and also just simple curiosity. – Gregory Higley Nov 23 '17 at 18:32
  • You can't not be worried about Objc. AnyObject _is_ Objc `id`. That is _what_ it is. – matt Nov 23 '17 at 18:34
  • Sure. My point is that _in my particular use case_, I'm not worried about Obj-C. But someone else might be and it is relevant. – Gregory Higley Nov 23 '17 at 18:35
  • @Hamish - Anything can be cast to `Any`. Only class types can be cast to `AnyObject`. – Rob Nov 23 '17 at 18:57
  • 1
    @Rob On Apple platforms, anything can bridge to `AnyObject`. Types that don't have a direct class counterpart are boxed in the opaque Obj-C compatible wrapper `_SwiftValue`. Therefore the cast always succeeds, and `is AnyObject` is always true (which is what is discussed in the Q&A I linked to). On Linux, it's a different story as there's no Obj-C interop; there `AnyObject` behaves just as you state. – Hamish Nov 23 '17 at 19:00
  • @Hamish - Hmm. [The Swift Programming Language](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID338) explicitly says “`Any` can represent an instance of any type at all, including function types. `AnyObject` can represent an instance of any class type.” – Rob Nov 23 '17 at 19:12
  • 1
    @Rob I mean it's technically correct; anything typed as `AnyObject` *is* a class instance. It's just that with Obj-C interop, `Any` is bridgeable to `AnyObject`. But note that the bridge needs to be made explicit with `as`, for example `let i: AnyObject = 5` is illegal but `let i = 5 as AnyObject` is legal. – Hamish Nov 23 '17 at 19:19
  • This interaction is exactly why this question should not be closed. – Gregory Higley Nov 24 '17 at 00:48

1 Answers1

4

AnyObject is Objective-C id, meaning any class type. Casting to AnyObject means "wrap this up in a way that Objective-C can deal with as a class type."

Obviously, an NSObject subclass just crosses the bridge directly.

For other types (i.e. nonclasses), Swift 4 follows three policies:

  • Some types are bridged directly to classes, e.g. String to NSString.

  • Some types are wrapped in Objective-C class types, e.g. Int in NSNumber or CGRect in NSValue.

  • All other types are boxed in an opaque wrapper; they can cross the bridge to Objective-C, and can be round-tripped successfully back to Swift, but nothing useful can be done with them in the Objective-C world.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Was this always the case in Swift? My understanding is that it was not. – Gregory Higley Nov 23 '17 at 18:38
  • 1
    @GregoryHigley Correct, it changed in Swift 3 [with `id` being imported as `Any`](https://github.com/apple/swift-evolution/blob/master/proposals/0116-id-as-any.md), so `Any` needed to bridgeable back to `id` (and casting to `AnyObject` forces this bridge). – Hamish Nov 23 '17 at 18:46
  • `AnyObject` means class types, doesn't it? `Int` will be wrapped to `NSNumber` only if the context does not allow for a value type. `as AnyObject` means "makes this a class type". – Sulthan Nov 23 '17 at 18:49
  • @Sulthan If you cast an Int (or anything else) to AnyObject you are asking for a class type. Objective-C `id` means a class. – matt Nov 23 '17 at 18:51
  • @matt Yes, I don't think that saying "wrap this up in a way that Objective-C can deal with" is very precise :) Obj-C can often deal with value types. – Sulthan Nov 23 '17 at 18:52
  • 1
    @GregoryHigley Right, it came in by the back door. In Swift 3 originally, for example, if you submitted a CGRect where an AnyObject was expected, nothing happened. So your code would compile but it might not work; wrapping as an NSValue was done for you. I filed a bug report with the Swift people and they added automatic NSValue wrapping. And then in Swift 4 they added boxing so everything could cross the bridge. – matt Nov 23 '17 at 18:53
  • @Sulthan ok, added "as a class type", feel free to suggest better wording. – matt Nov 23 '17 at 18:54
  • 1
    @GregoryHigley This project contains massive testing of all this https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/bk1ch14Appendix – matt Nov 23 '17 at 18:56