Why Does Somestruct() Is Anyobject Return True

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



Leave a reply



Submit