<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">I’ve been trying to figure out the rationale for why the code below behaves the way it does for some time:<div class=""><br class=""></div><div class=""><div class="">import Foundation</div><div class=""><br class=""></div><div class="">class C: NSObject {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>dynamic var foo: Int { return 5 }</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>dynamic func bar() -> Int { return 6 }</div><div class="">}</div><div class=""><br class=""></div><div class="">struct S {}</div><div class=""><br class=""></div><div class="">let c = C()</div><div class="">let s = S()</div><div class=""><br class=""></div><div class="">print(c is AnyObject) // warning: 'is' test is always true</div><div class="">print(s is AnyObject) // warning: 'is' test is always true (?!)</div><div class=""><br class=""></div><div class="">print(c as AnyObject) // <Project.C: 0x0123456789012345></div><div class="">print(s as AnyObject) // Project.S()</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; line-height: normal;" class="">print(c as? AnyObject) // warning: conditional cast from 'C' to 'AnyObject' always succeeds</div><div style="margin: 0px; line-height: normal;" class="">print(s as? AnyObject) // warning: conditional cast from 'S' to 'AnyObject' always succeeds</div></div><div class=""><br class=""></div><div class="">print(c as AnyObject? as Any) // Optional(<Project.C: 0x0123456789012345>)</div><div class="">print(s as AnyObject? as Any) // Optional(Project.S())</div><div class=""><br class=""></div><div class="">print((c as AnyObject?)?.foo as Any) // Optional(5)</div><div class="">print((s as AnyObject?)?.foo as Any) // nil</div><div class="">print((c as AnyObject?)?.bar() as Any) // Optional(6)</div><div class="">print((s as AnyObject?)?.bar() as Any) // crash! -[_SwiftValue bar]: unrecognized selector sent to instance 0x5432109876543210</div></div><div class=""><br class=""></div><div class="">So what we have is:</div><div class=""><br class=""></div><div class="">1. Any type you have will always claim it’s a class type, every time, even if it’s actually a non-bridgeable value type.</div><div class=""><br class=""></div><div class="">2. Conditional casting will also succeed, even if you use it on a non-bridgeable value type.</div><div class=""><br class=""></div><div class="">3. Non-conditional casting works too, despite that the underlying type might be a non-bridgeable value type.</div><div class=""><br class=""></div><div class="">4. Bridging to an optional will *also* always give you a value, even if what’s underneath is a non-bridgeable value type.</div><div class=""><br class=""></div><div class="">5. Trying to call a property on an optional from #4 will, surprisingly, work as you’d expect. The class type that implements the property returns the property, the value type returns nil.</div><div class=""><br class=""></div><div class="">6. Trying to call a method on an optional from #4 will crash.</div><div class=""><br class=""></div><div class="">This raises a few questions:</div><div class=""><br class=""></div><div class="">1. Why in the blazes is it implemented like this? Why not only allow conditional casting to AnyObject, which would only succeed if the type either actually was an object or could actually be bridged to an object? Why make the cast guaranteed, even when in actuality it’s not?</div><div class=""><br class=""></div><div class="">2. Why is there no obvious way to figure out whether something can actually be an object? The already kind of non-obvious “type(of: s) is AnyObject.Type” trick works to tell you if the thing is already a class, but not if something is bridgeable to a class; using it on a string, for example, returns false. And trying something like “type(of: s as AnyObject) is AnyObject.Type” returns true (?!), so that doesn’t work to detect bridgeable things.</div><div class=""><br class=""></div><div class="">3. Is the behavior in #5 guaranteed? i.e., is this safe:</div><div class=""><br class=""></div><div class=""><div class="">@IBAction func foo(_ sender: Any) {</div><div class=""> let tag = (sender as AnyObject?)?.tag ?? 0 // kinda ugly</div></div><div class=""><br class=""></div><div class=""> ...</div><div class="">}</div><div class=""><br class=""></div><div class="">or should we all be doing something like this:</div><div class=""><br class=""></div><div class="">@IBAction func foo(_ sender: Any) {</div><div class=""> let tag = type(of: sender) is AnyObject.Type ? (sender as AnyObject?)?.tag ?? 0 : 0 // holy cow, ugly</div><div class=""><br class=""></div><div class=""> …</div><div class="">}</div><div class=""><br class=""></div><div class="">(and yes, I did file a radar asking for a “contains tag” protocol so we wouldn’t have to mess with this stuff here. <a href="rdar://29623107" class="">rdar://29623107</a>)</div><div class=""><br class=""></div><div class="">Is there a reason it works this way? I’m tempted to write up a proposal to get rid of “as AnyObject” and just have people do an “as? AnyObject” which will work if it can be done and return nil otherwise, but I’m curious as to what the reasoning behind the current behavior is.</div><div class=""><br class=""></div><div class="">Charles</div><div class=""><br class=""></div></body></html>