Why does SomeStruct() is AnyObject return true?
Ugh, this is one of my gripes with Swift. It's an Objective C interop feature, which although useful, is too implicit/mysterious. This implicit boxing behaviour only happens when Foundation is imported, and only on systems with ObjC support (Apple's platforms).
Some Swift types bridge to specific ObjC counterparts, like NSString
, NSNumber
, NSArray
, NSDictionary
, etc. All other swift value types (like structs and tuples) are capable of being wrapped in a private ObjC class called _NSSwiftValue
, which makes it possible to hand them off to ObjC APIs.
The most confusing thing is that the object has an ill-defined identity (object address) as far as Objective C is concerned, and if your type doesn't conform to Hashable
, then the Hash value of the object is also ill-defined, which can lead to all kinds of hard-to-nail-down bugs.
Why is casting a struct to AnyObject not a compile error in swift?
This is a feature to facilitate passing to Cocoa. Any struct can be wrapped into a SwiftValue
reference type. If you print type(of: object)
you'll see the wrapper.
I don't think there is any contract for "expecting a reference type." More importantly, while "value types" and "reference types" exist in Swift, what really matter is value and reference semantics, which are not expressible in the language. You can create value semantics in reference types and reference semantics in value types, so the Swift type system really isn't of any help in that regard.
The important point here is that you only get this unusual behavior if you explicitly request it by asking for as AnyObject
. There are very few reason to write that, and if you are, you had better know exactly what you're doing.
Check if `Any` value is object
UPDATE
The code I have shown below is reported as not working in release build.
(Please see Paul Cantrell's comment below.)
Apologies for my "as far as I tested" was too limited.
I'll update this answer when I find some further info about this.
I'm not sure we can see this behaviour in the next beta (or GM or Released version...), but this works as you expect in Xcode 8 beta 6.
let foo: Any = 4
if type(of: foo) is AnyClass {
print("It's an object.")
let object = foo as AnyObject
//do something with `object` that requires reference semantics
} else {
print("It's not an object.") //->It's not an object.
}
class MyClass {}
let bar: Any = MyClass()
if type(of: bar) is AnyClass {
print("It's an object.") //->It's an object.
let object = foo as AnyObject
//do something with `object` that requires reference semantics
} else {
print("It's not an object.")
}
let baz: Any = Array<AnyObject>()
if type(of: baz) is AnyClass {
print("It's an object.")
let object = foo as AnyObject
//do something with `object` that requires reference semantics
} else {
print("It's not an object.") //->It's not an object.
}
I cannot check all possible cases, so there may be some edge cases where this does not work. But as far as I tested, this seems to work as expected.
Is it possible to replicate Swifts automatic numeric value bridging to Foundation (NSNumber) for (U)Int8/16/32/64 types?
Yes (it's possible): by conformance to protocol _ObjectiveCBridgeable
(The following answer is based on using Swift 2.2 and XCode 7.3.)
Just as I was pondering over whether to post or simply skip this question, I stumbled over swift/stdlib/public/core/BridgeObjectiveC.swift
in the Swift source code, specifically the protocol _ObjectiveCBridgeable
. I've briefly noticed the protocol previously at Swiftdoc.org, but in its current (empty) blueprint form in the latter, I've never given much thought to it. Using the blueprints for _ObjectiveCBridgeable
from the Swift source we can, however, swiftly let some native of custom type conform to it.
Before proceeding, note that _ObjectiveCBridgeable
is an internal/hidden protocol (_UnderScorePreFixedProtocol
), so solutions based on it might break without warning in upcoming Swift versions.
Enabling Int64
bridging to Foundation class NSNumber
As an example, extend Int64
to conform to _ObjectiveCBridgeable
, and subsequently test if this quite simple fix is sufficient for the implicit type conversion (bridging) from Int64
to Foundation class NSNumber
holds.
import Foundation
extension Int64: _ObjectiveCBridgeable {
public typealias _ObjectiveCType = NSNumber
public static func _isBridgedToObjectiveC() -> Bool {
return true
}
public static func _getObjectiveCType() -> Any.Type {
return _ObjectiveCType.self
}
public func _bridgeToObjectiveC() -> _ObjectiveCType {
return NSNumber(longLong: self)
}
public static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: Int64?) {
result = source.longLongValue
}
public static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: Int64?) -> Bool {
self._forceBridgeFromObjectiveC(source, result: &result)
return true
}
}
Test:
/* Test case: scalar */
let fooInt: Int = 42
let fooInt64: Int64 = 42
var fooAnyObj : AnyObject
fooAnyObj = fooInt // OK, natively
fooAnyObj = fooInt64 // OK! _ObjectiveCBridgeable conformance successful
/* Test case: array */
let fooIntArr: [Int] = [42, 23]
let fooInt64Arr: [Int64] = [42, 23]
var fooAnyObjArr : [AnyObject]
fooAnyObjArr = fooIntArr // OK, natively
fooAnyObjArr = fooInt64Arr // OK! _ObjectiveCBridgeable conformance successful
Hence, conformance to _ObjectiveCBridgeable
is indeed sufficient to enable automatic by-assignment bridging to the corresponding Foundation class; in this case, NSNumber
(in Swift, __NSCFNumber
).
Enabling Int8
, UInt8
, Int16
, UInt16
, Int32
, UInt32
, (Int64
), and UInt64
bridging to NSNumber
The above conformance of Int64
to _ObjectiveCBridgeable
can easily be modified to cover any of the Swift-native integer types, using the NSNumber
conversion table below.
/* NSNumber initializer: NSNumber native Swift type property
-------------------------------- -----------------------------------
init(char: <Int8>) .charValue
init(unsignedChar: <UInt8>) .unsignedCharValue
init(short: <Int16>) .shortValue
init(unsignedShort: <UInt16>) .unsignedShortValue
init(int: <Int32>) .intValue
init(unsignedInt: <UInt32>) .unsignedIntValue
init(longLong: <Int64>) .longLongValue
init(unsignedLongLong: <UInt64>) .unsignedLongLongValue */
Related Topics
Swift Programmatically Create Function for Button with a Closure
iOS Firebase: Firauthuidelegate.Authui Not Being Called
Implement Protocol Through Extension
Why Strings Are Not Equal in My Case
Extending a Protocol Where Self: Generic Type in Swift (Requires Arguments in <...>)
Swift Skshapenode Shapewithsplinepoints
Cast Any to Float Always Fails in Swift4.1
How to Properly Check If Non-Optional Return Value Is Valid
Swift Optional Type: How .None == Nil Works
Error with Parse Query Findobjectsinbackgroundwithblock
Split String by Components and Keep Components in Place
How to Prompt for Accessibility Features in a MACos App (From the Appdelegate)
Swift Firebase Storage How to Retrieve Image with Unknow Name(Nsuuid)