Swift 2: Why is it necessary to apply ‘as!’ after getting new object by ‘copy()’? - swift

I’ve created new SKEmitterNode object using copy() method. After that i’ve tried to write emitter.position but Xcode said «Ambiguous reference to member ‘position’». But, when i use type conversion «as! SKEmitterNode» after the «copy()», everything is ok. Can you explain me, please, why am i need to use «as!» in this case? I can’t understand this because when i check value type of «emit» variable in debugger, i can see that it’s already have the type SKEmitterNode, even without using «as! SKEmitterNode» after «copy()».
class GameScene: SKScene, SKPhysicsContactDelegate {
let bangEmitter : SKEmitterNode = SKEmitterNode(fileNamed: "MyParticle")!
func makeBang(position: CGPoint) {
// this method causes an error in second line
// but, emit is already have type SKEmitterNode, as debugger says
var emit = bangEmitter.copy()
emit.position = position
// this works ok
var emit = bangEmitter.copy() as! SKEmitterNode
emit.position = position
}
}

Because copy() is a method defined by NSObject and is meant to be overriden by subclasses to provide their own implementation. NSObject itself doesn't support it and will throw an exception if you call copy() on it.
Since it's meant for subclassing, there's no way to tell what the class of the object that will be returned. In Objective-C, it returns an id; in Swift, this becomes AnyObject. Since you, the programmer, know what kind of object you are copying from, you can use as! SomeClass to tell the compiler what kind of object the copy is.
This also speaks to the difference between ObjectiveC and Swift. Objective-C is dynamic. In Objective-C, every time you send a message, the run time will check if the object responds to the message. This happens at run time. In Swift, you call a method and this happens at compile time. The compiler must know the object's type in order to call the right function / method.
This explains why you get emit as an SKEmitterNode in the debugger - this is run time. The compiler doesn't know that at compile time.

Using the as! is an indicator that a check may fail.
Swift 1.2 separates the notions of guaranteed conversion and forced conversion into two distinct operators. Guaranteed conversion is still performed with the as operator, but forced conversion now uses the as! operator. The ! is meant to indicate that the conversion may fail. This way, you know at a glance which conversions may cause the program to crash.
Reference: https://developer.apple.com/swift/blog/?id=23

Look up the definition of the function copy() and you'll see that it always returns Any, therefore you always need to cast it to the object that you're seeking.

Related

How to get UnsafeRawPointer on the swift object?

My app uses the native C++ lib, there is a method that takes as an argument void*
void foo(void * obj) { ... }
in swift I can call this method like this
func boo(obj: MyCustomObj) {
foo(&obj)
}
and looks like really I get a void pointer on the object, but if I try to get an UnsafeRawPointer on the object like this
func boo(obj: MyCustomObj) {
var pointer = &obj <---- Use of extraneous '&'
foo(pointer)
}
I got an error
Use of extraneous '&'
What is the problem here?
EDIT
I understood that using withUnsafe*** I can get the pointer to the data, but what to do if my method has 2 params, would it looks like this
withUnsafePointer(to: myObjFirst) {
pFirst in
withUnsafePointer(to: myObjSecond) {
pSecond in
foo(pFirst, pSecond)
}
}
The & syntax does not mean "the address of" or "pointer to" like in C. In Swift, it is an in-out expression.
These can be used to create implicit pointer conversions as a convenience, and that can seem like C's "pointer to" meaning, but it has very different rules and behaviors. For example, there is no promise that obj even has an address. It may be a tagged pointer. Passing it via an in-out expression may allocate memory and copy the value to make the call possible. Similarly, when passing a "pointer to an array," Swift will actually pass a pointer to a contiguous block of values (which may have been copied to make them contiguous) which is not the same as the actual Array struct.
It is not meaningful to say var pointer = &obj. There is no in-out reference there.
There is no general way to take long-lived pointers to objects in Swift without allocating your own memory (and this is rare). The memory model doesn't promise the kinds of lifetimes you'd need to make that sensible. If your code did compile the way you expect it to, the call to foo(pointer) would still be invalid because there's no promise that obj exists at that point and the pointer could be dangling. (There are no references to obj after the first line, so Swift can and often will destroy it, even though it's still "in scope.")
The foo(&obj) syntax is basically a shorthand for:
withUnsafePointer(to: obj) { foo($0) }
It exists to make it easier to call C functions, but it doesn't mean that Swift pointers are anything like C pointers.
For much more on Swift pointers, see Safely manage pointers in Swift from WWDC 2020.

How do I manually retain in Swift with ARC?

I'm using Swift 3 with ARC in an iOS app, and I want to manually retain an object.
I tried object.retain() but Xcode says that it's unavailable in ARC mode. Is there an alternative way to do this, to tell Xcode I know what I'm doing?
Long Version:
I have a LocationTracker class that registers itself as the delegate of a CLLocationManager. When the user's location changes, it updates a static variable named location. Other parts of my code that need the location access this static variable, without having or needing a reference to the LocationTracker instance.
The problem with this design is that delegates aren't retained, so the LocationTracker is deallocated by the time the CLLocationManager sends a message to it, causing a crash.
I would like to manually increment the refcount of the LocationTracker before setting it as a delegate. The object will never be deallocated anyway, since the location should be monitored as long as the app is running.
I found a workaround, which is to have a static variable 'instance' that keeps a reference to the LocationTracker. I consider this design inelegant, since I'm never going to use the 'instance' variable. Can I get rid of it and explicitly increment the refcount?
This question is not a duplicate, as was claimed, since the other question is about Objective-C, while this one is about Swift.
The solution turned out to be to re-enable retain() and release():
extension NSObjectProtocol {
/// Same as retain(), which the compiler no longer lets us call:
#discardableResult
func retainMe() -> Self {
_ = Unmanaged.passRetained(self)
return self
}
/// Same as autorelease(), which the compiler no longer lets us call.
///
/// This function does an autorelease() rather than release() to give you more flexibility.
#discardableResult
func releaseMe() -> Self {
_ = Unmanaged.passUnretained(self).autorelease()
return self
}
}
This is easily done with withExtendedLifetime(_:_:) function. From the documentation:
Evaluates a closure while ensuring that the given instance is not destroyed before the closure returns.
Cheers!

node.physicsBody.joints downcasting error

The following code gives an error - it appears the physics joints array have the class PKPhysicsJoint. Anyone have any ideas how I can iterate through the joints in Swift?
The documentation does say that physicsBody.joints should return an array of SKPhysicsJoint.
import SpriteKit
let scene = SKScene(size: CGSize(width: 200, height: 200))
let nodeA = SKNode()
let nodeB = SKNode()
nodeA.physicsBody = SKPhysicsBody(circleOfRadius: 20)
nodeB.physicsBody = SKPhysicsBody(circleOfRadius: 20)
scene.addChild(nodeA)
scene.addChild(nodeB)
let joint = SKPhysicsJointFixed.jointWithBodyA(nodeA.physicsBody, bodyB: nodeB.physicsBody, anchor: CGPointZero)
scene.physicsWorld.addJoint(joint)
for joint in nodeA.physicsBody!.joints as [SKPhysicsJoint] {
// do something else here
}
gives error:
Execution was interrupted. reason: EXC_BAD_INSTRUCTION...
Update: This was a bug, and it's fixed in iOS 9 / OS X 10.11 — the code in the question just works now.
Leaving original answer text for posterity / folks using older SDKs / etc.
This looks like a bug — you should file it. Whether it should be considered a SpriteKit bug or a Swift bug is hard to say, but that's Apple's problem, not yours. :)
The problem is clear if you paste your code into a playground — your joint is actually a PKPhysicsJointWeld behind the scenes. That's some internal class that should be an implementation detail. In ObjC that's no problem, because casting in C is just a matter of telling the compiler, "trust me, this pointer is really an SKPhysicsJoint, so let me call physics joint methods (and nothing else) on it and and no one will be the wiser". Casting in Swift requires that there be a type hierarchy relationship between the casted types — and PKPhysicsJointWeld is not a subtype/subclass of SKPhysicsJoint, so the cast fails.
You can work around this issue by avoiding the cast to [SKPhysicsJoint]:
for joint in nodeA.physicsBody!.joints {
// do something else here
}
With this, you lose some type safety — joint is an AnyObject, so like ObjC's id type the compiler lets you call any method on it. (And it may fail at runtime if that object doesn't implement the method.) But at least it runs.
A further workaround: inside the loop, you can cast joint to SKPhysicsJoint. But since that cast is across the type hierarchy, you have to use unsafeBitCast:
for joint in nodeA.physicsBody!.joints {
let skJoint = unsafeBitCast(joint, SKPhysicsJoint.self)
// do stuff with skJoint
}
This gets you back a little bit of compile-time type "safety", in that thereafter the compiler will require anything you do with skJoint to be compatible with SKPhysicsJoint, but it's still inherently unsafe in that it depends on some hand-waving around runtime types that may not always hold. And you have to unsafeBitCast again to get to a particular joint subclass, without knowing which subclass it might be. (Again, this would be a good time to file a bug.)
(You might notice from pasting into a playground that physicsWorld is of an internal class, too: PKPhysicsWorld. So why doesn't that fail, too? When you use the physicsWorld property, all the type casting happens on the ObjC side, and Swift trusts whatever ObjC tells it. When you deal with the joints array, though, you have to do a type cast on the Swift side, and Swift is much more strict about type checking.)

Bad Access in `top_level_code` when I set instance variable

Ok, this is an odd error and it's taken me many hours to track down the exact location (although the cause remains unknown). The error only occurs on 64-bit devices. In other words, it works fine on my iPhone 5, but crashes on my iPhone 6 Plus.
Essentially, I have a private variable in a class:
class Manager {
private lastUpdated:NSDate?
}
Initially, it's not set. This class retrieves messages from a server and then sets the lastUpdated variable to NSDate().
private func checkForNewMessages(complete:Result<[Message]> -> ()) {
self.server.userMessages(u, fromDate: self.lastUpdated) { result in
result.onSuccess { messages in
self.insertMessages(messages)
self.lastUpdated = NSDate()
complete(Result(messages))
}
result.onError { err in complete(Result(err)) }
}
}
I've deleted extraneous code, but nothing inside the code path that's crashing (just stuff around it)
Note: I'm using a Result enum. For more info about this kind of enum, this article is pretty good at explaining it: Error Handling in Swift.
The first time the manager retrieves messages, everything works fine and the lastUpdated value is set correctly. If I then try to retrieve messages again, the app crashes when it tries to set self.lastUpdated = NSDate().
I can get it to work if I delay the assignment by using GCD, dispatch_after. But I don't want to do that.
Does anyone have any idea why this is occurring?
Update
This is only occurring when I assign a NSDate. It's not occurring if I try to set another object type (Int, Bool, String) etc.
The crash occurs if I change the stored variable to NSTimeInterval and attempt to set it from NSDate.timeIntervalSinceReferenceDate()
The crash occurs if I store an array of updates and attempt to simply append a new date to that array.
The crash occurs if I attempt to set lastUpdated before the server is called rather than in the callback.
The crash occurs if I wrap NSDate in another class.
The crash occurs is I set lastUpdated in a background thread
The crash doesn't occur if I println(NSDate()) (removing the assignment)
Ok, it took me numerous hours but I finally figured it out.
This will be a little difficult to explain and I'm not certain I fully understand what is going on. The error actually occurred in the Server class that was making the request. It took the lastUpdated date, converted it to a string and then sent that up to the server. The first run, there was no date, so no issue. In the second run, it took the date and used a date formatter to convert it to a string. However, I thought that stringFromDate returned an Optional. It does not. Normally, the compiler would warn me if I attempted to unwrap something that wasn't an option. However, I use Swift's functional aspects to use Infix operators to unwrap and conditionally pass the unwrapped value to another function, which can then pass it's value on. It allows me to chain unwraps together so I can avoid "nested if-let hell".
Very simple but powerful:
infix operator >>? { associativity left}
func >>? <U,T>(opt: T?, f: (T -> U?)) -> U? {
if let x = opt {
return f(x)
}
return nil
}
Note that the function must return an Optional. For some reason, the Swift compiler missed the fact that stringFromDate did not return an optional and so it didn't warn me. I'm not sure why that is, it's certainly warned me before:
if let update = fromDate >>? self.dateFormatter.stringFromDate {
This fails, but not immediately
Unwrapping a non-optional value doesn't immediately result in a crash, error or anything apparently. I successfully used the date string to send and receive data from the server. I'm not certain why unwrapping a non-optional string would later result in a crash and that only when I attempted to set the instance variable that of the backing NSDate object the string was generated from. I think somehow, unwrapping a non-optional screwed up some pointers, but not necessarily the pointer to the unwrapped object. I think the crash itself has to do with Swift attempting to release or dealloc the original instance variable (lastUpdated) when a new one is set, but found the memory address messed up. I think only a Apple Swift engineer could tell me what actually happened, but perhaps there are clues here to how Swift works internally.

How is a return value of AnyObject! different from AnyObject

The NSMetadataItem class in the Cocoa framework under Swift contains the following function:
func valueForAttribute(key: String!) -> AnyObject!
I'm still learning the difference (and details) between forced unwrapping and optional chaining. In the above function, does this mean:
The key parameter must have a value, and
The return value is guaranteed to have a value?
My primary concern is with the exclamation point following the return value - once I have assigned the return value:
var isDownloadedVal = item.valueForAttribute(NSMetadataUbiquitousItemIsDownloadedKey)
Do I need to include an if let block when examining it, or am I guaranteed that it will have a value I can examine safely?
TLDR: Treat Foo! as if it were Foo.
Many Cocoa calls include implicitly unwrapped optionals, and their need for it is very likely the reason the feature even exists. Here's how I recommend thinking about it.
First, let's think about a simpler case that doesn't involve AnyObject. I think UIDevice makes a good example.
class func currentDevice() -> UIDevice!
What's going on here? Well, there is always a currentDevice. If this returned nil that would indicate some kind of deep error in the system. So if we were building this interface in Swift, this would likely just return UIDevice and be done with it. But we need to bridge to Objective-C, which returns UIDevice*. Now that should never be nil, but it syntactically could be nil. Now in ObjC, we typically ignore that fact and don't nil-check here (particularly because nil-messaging is typically safe).
So how would we express this situation in Swift? Well, technically it's an Optional<UIDevice>, and you'd wind up with:
class func currentDevice() -> UIDevice?
And you'd need to explicitly unwrap that every time you used it (ideally with an if let block). That would very quickly drive you insane, and for nothing. currentDevice() always returns a value. The Optional is an artifact of bridging to ObjC.
So they invented a hack to work around that (and I think it really is a hack; I can't imagine building this feature if ObjC weren't in the mix). That hack says, yes, it's an Optional, but you can pretend it's not, and we promise it's always going to be a value.
And that's !. For this kind of stuff, you basically ignore the ! and pretend that it's handing you back a UIDevice and roll along. If they lied to you and return nil, well, that's going to crash. They shouldn't have lied to you.
This suggests a rule: don't use ! unless you really need to (and you pretty much only need to when bridging to ObjC).
In your specific example, this works in both directions:
func valueForAttribute(key: String!) -> AnyObject!
Technically it takes an Optional<String>, but only because it's bridged to NSString*. You must pass non-nil here. And it technically returns you Optional<AnyObject>, but only because it's bridged to id. It promises that it won't be nil.
According to the Swift-eBook, which states the following
„Trying to use ! to access a non-existent optional value triggers a runtime error. Always make sure that an optional contains a non-nil value before using ! to force-unwrap its value.“
I would answer to your first two questions with Yes.
Do I need to include an if let block when examining it...
No, this is not necessary.