AnyObject not working in Xcode8 beta6?
Asked Answered
S

2

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)")
    }
    //...
}
Shod answered 19/8, 2016 at 7:16 Comment(11)
Looks like a compiler warn system bug. Structs can not conform to class protocol AnyObjectNuncupative
Nice find – you should file a bug reportGrosberg
Thanks. Bug report: bugs.swift.org/browse/SR-2420Shod
I saw your SwiftNoficationCenter some time ago and (unsuccessfully) tried to make that runtime check a compile time check: #37707950. 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.Disaffirm
@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.Shod
@100mango: You are right, there is no strong reference to the object. I didn't think of that.Disaffirm
@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.Disaffirm
@100mango: Another option could be to change the function parameters to func register<T>(_ protocolType: T.Type, observer: AnyObject) , and check the protocol conformance at runtime, e.g. guard observer is T else { fatalError() }.Disaffirm
@MartinR Thanks. I am considering making T conform to AnyObject or the way as you propose.Shod
Have a look at #39185411, perhaps that is a useful solution for your problem.Disaffirm
@MartinR Thanks. I will try to test it as soon as possible. I have created a Swift3 branch for SwiftNotificationCenter. Thanks again.Shod
O
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.

Organon answered 19/8, 2016 at 8:11 Comment(11)
Thanks for your reply. I filed a bug report: bugs.swift.org/browse/SR-2420. The reason why I don't constraint T with AnyObject is that I created a framework SwiftNotificationCenter for one to many communication. And user will pass Protocol.Type as parameter.Shod
@Grosberg as always, thanks for grammar/typo corrections! :)Organon
struct SomeStruct { } ; let o = SomeStruct() as AnyObject does not fail at runtime.Disaffirm
The magic seems to be in github.com/apple/swift/blob/master/stdlib/public/runtime/….Disaffirm
@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!Organon
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.Disaffirm
@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, 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.Organon
Yes, that's how I understand it.Disaffirm
@MartinR Thanks. As a final note, it seems as _ObjectiveCBridgeable 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).Organon
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.Disaffirm
@MartinR You're looking for https://mcmap.net/q/456261/-define-struct-that-is-treated-like-a-class-in-swift :)Grosberg
R
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:

Rationalism answered 10/11, 2016 at 23:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.