25

In Xcode8 beta6, the following code will cause a warning: 'is' test is always true. But it won't print pass.

    struct TestStruct {
    }

    //warning: 'is' test is always true
    if TestStruct() is AnyObject {
        print("pass")
    }

And the following code will cause a warning: Conditional cast from 'T' to 'AnyObject' always succeeds

public static func register<T>(_ protocolType: T.Type, observer: T) {

    //Warning: Conditional cast from 'T' to 'AnyObject' always succeeds
    guard let object = observer as? AnyObject else {
        fatalError("expecting reference type but found value type: \(observer)")
    }
    //...
}
Learing
  • 559
  • 4
  • 13
  • 2
    Looks like a compiler warn system bug. Structs can not conform to class protocol AnyObject – Yury Aug 19 '16 at 07:27
  • Nice find – you should [file a bug report](https://bugs.swift.org) – Hamish Aug 19 '16 at 07:43
  • Thanks. Bug report: https://bugs.swift.org/browse/SR-2420 – Learing Aug 19 '16 at 08:59
  • I saw your SwiftNoficationCenter some time ago and (unsuccessfully) tried to make that runtime check a compile time check: http://stackoverflow.com/questions/37707450/function-that-takes-a-protocol-and-a-conforming-class-instance-as-parameters. But now (as I understand it) *anything* can be wrapped into an object (with weak references, as needed in your framework). So registering values as observer should just work. – Martin R Aug 20 '16 at 10:36
  • @MartinR Thanks for your reply. But it doesn't work. Once the _SwiftValue is assigned to weak reference. The weak reference become nil. I don't know what happen in the underlying mechanism, But I don't think value type can be observed, because it's passed by copy. – Learing Aug 22 '16 at 05:17
  • @100mango: You are right, there is no strong reference to the object. I didn't think of that. – Martin R Aug 22 '16 at 05:28
  • @100mango: This could work: `weak var weakRef = observer as AnyObject; guard let object = weakRef else { ... }`. – It is a horrible workaround though, and I don't know if it is reliable. – Martin R Aug 22 '16 at 16:18
  • @100mango: Another option could be to change the function parameters to `func register(_ protocolType: T.Type, observer: AnyObject) `, and check the protocol conformance at runtime, e.g. `guard observer is T else { fatalError() }`. – Martin R Aug 24 '16 at 13:32
  • @MartinR Thanks. I am considering making T conform to AnyObject or the way as you propose. – Learing Aug 25 '16 at 05:02
  • Have a look at http://stackoverflow.com/questions/39184911/check-if-any-value-is-object, perhaps that is a useful solution for your problem. – Martin R Aug 27 '16 at 20:29
  • @MartinR Thanks. I will try to test it as soon as possible. I have created a Swift3 branch for SwiftNotificationCenter. Thanks again. – Learing Aug 28 '16 at 13:00

2 Answers2

9

The warning works as intended: the false return of TestStruct() is AnyObject, however, does not

The prior version of this answer perceived the warning,

'is' test is always true

as the bug, and contained some discussion as to why this perceived buggy warning would manifest itself. That TestStruct() is AnyObject evaluated to false at runtime, however, was perceived as expected behaviour.

Given the comments to the bug report filed by the OP (SR-2420), it seems the situation is the reverse: since Xcode 8/beta 6, the is test should always evaluate to true, and the bug the OP:s post is the fact that TestStruct() is AnyObject evaluates to false during runtime.

Joe Groff writes:

This is correct, because everything bridges to AnyObject now.

...

is/as AnyObject always succeed for all types now. It's behaving as intended.


The new SwiftValue box for conversion from Swift values to Obj-C objects

(for additional details, see discussion in the comments below, thanks @MartinR)

It seems as if Swift values that are not explicitly implemented to be bridgeable to Obj-C objects via e.g. conformance to _ObjectiveCBridgeable (see e.g. the following Q&A for details regarding _ObjectiveCBridgeable), will instead automatically make use of the new SwiftValue box to allow conversion to Obj-C objects.

The initial commit message for swift/stdlib/public/runtime/SwiftValue.mm reads:

Runtime: Implement an opaque 'SwiftValue' ObjC class to hold bridged values

If there's no better mapping for a Swift value into an Objective-C object for bridging purposes, we can fall back to boxing the value in a class. This class doesn't have any public interface beyond being NSObject-conforming in Objective-C, but is recognized by the Swift runtime so that it can be dynamically cast back to the boxed type.

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • 1
    Thanks for your reply. I filed a bug report: https://bugs.swift.org/browse/SR-2420. The reason why I don't constraint T with AnyObject is that I created a framework [SwiftNotificationCenter](https://github.com/100mango/SwiftNotificationCenter) for one to many communication. And user will pass Protocol.Type as parameter. – Learing Aug 19 '16 at 09:12
  • @Hamish as always, thanks for grammar/typo corrections! :) – dfrib Aug 19 '16 at 09:42
  • `struct SomeStruct { } ; let o = SomeStruct() as AnyObject` does *not* fail at runtime. – Martin R Aug 19 '16 at 18:14
  • 1
    The magic seems to be in https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftValue.mm. – Martin R Aug 19 '16 at 18:46
  • @MartinR Thanks, I don't have Xcode 8/beta 6 myself, so I never had a chance to try. Maybe I misunderstands your first comment, but note that I refer to OP's 2nd example (attempted `as?` conversion), when mentioning the expected (runtime) failed conversion (-> `nil`): `let o = SomeStruct() as? AnyObject` results in `nil` both in Xcode 7/Swift 2.2 and IBM Sandbox/Swift 3.0-dev (maybe _fail_ was a bad word choice in my answer). Does this attempted conversion succeed in Xcode 8/beta 6? Thanks for the repo link! – dfrib Aug 19 '16 at 19:14
  • 1
    Yes, the cast from my above comment from a pure Swift value to AnyObject *succeeds,* and `type(of: o)` prints "_SwiftValue". That's how I found that source file. So it seems that arbitrary values are boxed as AnyObject. – Martin R Aug 19 '16 at 19:33
  • 1
    @MartinR Ah, new magic in beta 6, didn't know about this. Just noticed that the bug report filed by OP had some activity, mainly linking to [this pull request](https://github.com/apple/swift/pull/4410), with commits to, among other, the SwiftValue source you linked to above (description: _"Dynamic casts to AnyObject should succeed for an arbitrary source type because we can always make a SwiftValue."_). So seems as the warning in OPs question is not a bug, but rather the fact that the `is` test fails at runtime. – dfrib Aug 19 '16 at 19:37
  • Yes, that's how I understand it. – Martin R Aug 19 '16 at 19:40
  • 1
    @MartinR Thanks. As a final note, it seems as [`_ObjectiveCBridgeable`](https://github.com/apple/swift/blob/master/stdlib/public/core/BridgeObjectiveC.swift) will still persist as detail-implemented implicit conversions, whereas the `_SwiftValue` box will be used if the prior is not available (for some given type). – dfrib Aug 19 '16 at 19:49
  • If I remember correctly, there was an answer posted recently (by Rob Napier?) with a link to an evolution thread about making that bridging protocol public (which was postponed). But I cannot find that answer anymore. – Martin R Aug 19 '16 at 19:59
  • @MartinR You're looking for http://stackoverflow.com/a/38837505/2976878 :) – Hamish Aug 19 '16 at 20:03
6

Long story short.

To check if value has a reference type:

if type(of: value) is AnyClass {
    // ...
}

To check if type is a reference type:

if SomeType.self is AnyClass {
   // ...
}

More helpful answers:

Community
  • 1
  • 1
mixel
  • 25,177
  • 13
  • 126
  • 165