1
let f: () -> Void = { }

let array = ["a", 1, false, f] as [Any]

if array[3] is AnyObject {
  print(array[3])
}

Why is the element evaluating to true for AnyObject even though the array is set to store Any?

Why is function evaluates to true as an AnyObject even though AnyObject by definition can only be classes?

As another example:

let f: () -> Bool = { return true }
let ff = f as AnyObject
(ff as () -> Bool)()

This goes against the definition of AnyObject in the API doc, which states that:

AnyObject can be used as the concrete type for an instance of any class, class type, or class-only protocol.

or the official Swift Programming Language Guide:

• Any can represent an instance of any type at all, including function types.

• AnyObject can represent an instance of any class type.

In the above example, it looks like function can be representated as AnyObject.

There is explanation elsewhere in SO (as pointed out by @hamish) that internally because SwiftValue class is used, anything can be bridged to AnyObject. The logic of explanation seems to be flawed/reverse, as we should make implementation conform to the language definition, not the other way around, so either the implementation is incorrect, or the definition of AnyObject and typecheck operator is incorrect?

Community
  • 1
  • 1
Boon
  • 40,656
  • 60
  • 209
  • 315
  • 2
    Because *everything* is bridgeable to `AnyObject` in Swift 3. See [AnyObject not working in Xcode8 beta6?](http://stackoverflow.com/q/39033194/2976878) – Hamish Feb 14 '17 at 20:41
  • 1
    Why and how is function bridgeable to AnyObject? Doesn't that break the definition of AnyObject? This behavior seems wrong even if it is possible. – Boon Feb 14 '17 at 20:58
  • 2
    Read the linked Q&A – things that are directly incompatible with Obj-C are boxed in the Objc-C compatible box `_SwiftValue`. The reasoning for this is because of the fact that `id` is now bridged to Swift as `Any`, so `Any` needs to be bridgeable back to `id`. – Hamish Feb 14 '17 at 21:02
  • I can understand that applying between id and Any. But why AnyObject? – Boon Feb 14 '17 at 21:08
  • 1
    Because `_SwiftValue` is a class (must be in order to be compatible with Obj-C), and therefore is an `AnyObject`. – Hamish Feb 14 '17 at 21:10
  • 1
    I understand the explanation but not the logic. Why should something like an "is cast" necessitates the need to explain internal implementation behavior to justify the behavior? Shouldn't the behavior be defined at language level - and internal implementation be made to conform to it? – Boon Feb 14 '17 at 21:12
  • 1
    In very general terms, probably, but I wouldn't agree around `Any` and `AnyObject`. If you're using those types extensively, you're either working with Cocoa (which is a transient and improving situation), or you're probably doing something wrong. Types like `[Any]` should be strongly avoided in Swift. But when having to deal w/ Cocoa, it makes sense to make it as convenient and transparent (bridged) as possible. – Rob Napier Feb 14 '17 at 23:39
  • @RobNapier Most definitions I have seen regarding AnyObject and type check operator seem incomplete/inaccurate with regards to actual behavior of AnyObject. For example, API doc says "AnyObject can be used as the concrete type for an instance of any class, class type, or class-only protocol." This seems grossly misleading as the behavior in this post is not implied in any way. – Boon Feb 15 '17 at 01:12
  • I assume you've imported `Foundation`, correct? It changes the behavior of `AnyObject`. I'm not saying it's documented, I'm saying that `AnyObject` is a deep morass of madness that unless you need it for Cocoa bridging, you probably shouldn't be using anyway, and it will probably change in weird ways in the future any time some feature makes bridging to Cocoa more useful. Not defending Swift on this, just how it tends to be. – Rob Napier Feb 15 '17 at 01:19
  • @RobNapier Didn't import Foundation. I thought Implicit bridging conversion had been eliminated to avoid confusion. What different behavior will import Foundation create for AnyObject? BTW I have listed example of casting function to AnyObject, even though by definition that belongs in the Any realm. – Boon Feb 15 '17 at 01:30
  • I'm not clear where you're going with all this. If you think the docs are wrong, open a defect (bugs.swift.org). Is there an actual question here? Yes, there is boxing. Yes, AnyObject bridges in ways that do not exactly conform to the text you quoted. Swift is not perfectly defined by its docs. If there's a conflict, open a defect, but I don't think there's a question here for Stack Overflow. – Rob Napier Feb 15 '17 at 03:16
  • @RobNapier I was having problem understanding the behavior and the definition in the programming guide. The actual question is why AnyObject behaves almost like Any, even though the doc suggests otherwise. How would you rephrase the definition of AnyObject vs Any (as currently defined in the Swift Programming Language) if you were to author it? Will be happy to accept that as the answer to this post. – Boon Feb 15 '17 at 09:13

1 Answers1

1

First, the Swift Programming Language Guide is a not a language specification in the way that ISO 9899 defines C. (And even given an ISO standard, not every compiler implements C identically, or even in 100% accordance with the standard.) If you find a disagreement between the compiler and the documentation, it is just as likely to be a documentation bug as a compiler bug.

That said, I believe you've glossed over an important part of the spec you reference:

AnyObject can also be used as the concrete type for an instance of a type that bridges to an Objective-C class. Many value types in Swift bridge to Objective-C counterparts, like String and Int.

() -> Void is equivalent to dispatch_block_t, which bridges to ObjC as a dispatch_object (see dispatch/object.h and os/object.h):

/*
 * By default, dispatch objects are declared as Objective-C types when building
 * with an Objective-C compiler. This allows them to participate in ARC, in RR
 * management by the Blocks runtime and in leaks checking by the static
 * analyzer, and enables them to be added to Cocoa collections.
 * See <os/object.h> for details.
 */
OS_OBJECT_DECL_CLASS(dispatch_object);

So there is no surprise here that () -> Void can be coerced into AnyObject.

In practice, pretty much anything can now bridge into AnyObject (again, from a language specification point of view, anything that can be an NSValue can be an AnyObject, even though that's not exactly how it's implemented).

AnyObject is different than Any, however. Any behaves like a protocol (despite not being a protocol). AnyObject behaves like the superclass of every class (despite actually being a protocol).

let b = true                         // true
let bany = true as Any               // true
let banyobj = true as AnyObject      // 1 <=== (because it's NSNumber)

MemoryLayout.size(ofValue: b)        // 1 (size of a bool)
MemoryLayout.size(ofValue: bany)     // 32 (size of a protocol box)
MemoryLayout.size(ofValue: banyobj)  // 8 (size of a reference pointer)

type(of: b)                          // Bool.Type
type(of: bany)                       // Bool.Type
type(of: banyobj)                    // __NSCFBoolean.Type

(Try the same thing for {} to see how closures are handled.)

It is reasonable to open a defect against the AnyObject docs to include a more explicit explanation that any type can be converted into a reference type using as AnyObject, but this is just an omission, not a contradiction of what's already there. (And if it were a contradiction, or you find it confusing, then again, the correct answer is to open a defect to improve the docs to match Swift, not Swift to match the docs.)

Rob Napier
  • 286,113
  • 34
  • 456
  • 610