Swift: Cannot flush AnyObject in SKPhysicsBody

Apple has the following method in the SKPhysicsBody class.

/* Returns an array of all SKPhysicsBodies currently in contact with this one */ func allContactedBodies() -> [AnyObject]! 

I noticed that it returns an AnyObject array. So I read about how to deal with AnyObject top-down casting. Here

I want to iterate over the allContactedBodies array of my physical body. The problem is that no matter what I try, I just can't get it to work.

I tried this first:

 for body in self.physicsBody.allContactedBodies() as [SKPhysicsBody] { } 

But I get this error.

fatal error: array cannot be converted to an array of derivatives

I also tried this:

 for object in self.physicsBody.allContactedBodies() { let body = object as SKPhysicsBody } 

But it also crashes with the following:

enter image description here

And similarly, I tried this:

  for object in self.physicsBody.allContactedBodies() { let body = object as? SKPhysicsBody } 

There is no collapse, but the "body" becomes zero.

And if I donโ€™t give up at all, I will not fail. For instance:

 for object in self.physicsBody.allContactedBodies() { } 

But obviously, I need to include if I want to use the actual type.


So, as a test, I just tried this:

 let object: AnyObject = SKPhysicsBody() let body = object as SKPhysicsBody 

And this also leads to the same failure as in the picture.


But other types will not break. For example, this will not work.

 let object: AnyObject = SKNode() let node = object as SKNode 

So my question is: how can I properly scroll through the allContactedBodies array ?

Edit: I am running Xcode 6 beta 4 on an iOS 8 beta 4 device.

Edit 2: Additional Information

Ok, so I just did a few more tests. I tried this:

 let bodies = self.physicsBody.allContactedBodies() as? [SKPhysicsBody] 

If "allContactedBodies" is empty, then the transfer is successful. But if "allContactedBodies" contains objects, then it is reset, and the "bodies" become zero, so I can not miss it. It seems like it is currently impossible to add AnyObject to SKPhysicsBody, which makes it impossible to scroll through the allContactedBodies array unless someone can provide a workaround.

Edit 3: Error in Xcode 6 beta 5. Workaround posted below still works
Edit 4: Error in Xcode 6 beta 6. Workaround posted below still works
Edit 5: Disappointed. Error in Xcode 6 GM. The workaround posted below still works

EDIT 6: I received the following message from Apple:

Engineering provided the following information:

We believe this issue was addressed in the latest version of Xcode 6.1.

BUT THIS IS NOT, the error is still in Xcode 6.1.1 !!! The workaround still works.

Edit 7: Xcode 6.3, still not fixed, workaround still works.

+6
source share
2 answers

After much trial and error, I found a workaround to my problem. Turns out you don't need to overturn access to SKPhysicsBody properties at all when the type is AnyObject.

 for object in self.physicsBody.allContactedBodies() { if object.node??.name == "surface" { isOnSurface = true } } 
+7
source

Update: This was a bug, and it was fixed in iOS 9 / OS X 10.11. The following code should only work now:

 for body in self.physicsBody.allContactedBodies() { // inferred type body: SKPhysicsBody print(body.node) // call an API defined on SKPhysicsBody } 

Leave the source text of the answer for posterity / people using old SDKs / etc.


I noticed this in the sidebar of related questions, answering this one , and this turns out to be the same underlying problem. So, while Epic Byte has a workable workaround , here is the root of the problem, why the workaround works, and some additional workarounds ...

Not that you cannot drop AnyObject into SKPhysicsBody in general - it's that the item (s) hiding behind these specific AnyObject links cannot be attributed to SKPhysicsBody .

The array returned by allContactedBodies() actually contains PKPhysicsBody objects, not SKPhysicsBody . PKPhysicsBody not a public API - presumably, it should be a detail of an implementation that you do not see. In ObjC, it's great to distinguish PKPhysicsBody * from SKPhysicsBody * ... it will "just work" if you only call the methods with which these two classes are shared. But in Swift, can you use as / as? / as! only up or down the type hierarchy, and PKPhysicsBody and SKPhysicsBody are not the parent class and subclass.

You get the error message let obj: AnyObject = SKPhysicsBody(); obj as SKPhysicsBody let obj: AnyObject = SKPhysicsBody(); obj as SKPhysicsBody , because even the SKPhysicsBody initializer returns PKPhysicsBody . Most of the time you donโ€™t need to go through this dance (and it fails) because you get a single SKPhysicsBody back from the initializer or method that claims to return SKPhysicsBody - all manual wave casting between SKPhysicsBody and PKPhysicsBody happens on the ObjC side, and Swift it trusts the imported ObjC API (and accesses the original API through the ObjC runtime, so it works the same as in ObjC, despite the type mismatch).

But when you create entire arrays, casting of runtime types should occur on the Swift side, therefore more stringent type checking rules come into play. Bringing an instance of PKPhysicsBody to SKPhysicsBody does not comply with these rules, so you will crash. You can pass an empty array to [SKPhysicsBody] without errors, because there are no objects of a conflicting type in the array (there are no objects in the array).

The Epic Byte AnyObject works because Swift AnyObject works like an ObjC id type: the compiler allows you to call methods of any class on it, and you just hope that at runtime you are dealing with an object that actually implements these methods.

You can return a bit of compilation type security by explicitly forcing the side:

 for object in self.physicsBody.allContactedBodies() { let body = unsafeBitCast(object, SKPhysicsBody.self) } 

After this, the body is SKPhysicsBody , so the compiler will only let you name SKPhysicsBody methods on it ... it behaves like ObjC casting, so you still hope that the methods you call are actually implemented by the object you are talking to. But at least the compiler can help you honestly. (You cannot unsafeBitCast an array type, so you must do this with an element inside a loop.)

This should probably be considered a mistake, so let Apple know if it affects you.

+5
source

Source: https://habr.com/ru/post/972988/


All Articles