I'm currently upgrading a forked Framework to Swift 3.
public convenience init<Object: AnyObject>(object: Object,
keyPath: String,
options: NSKeyValueObservingOptions = [],
block: @escaping (_ object: Object, _ change: Change, _ kvo: KVObserver) -> Void) {
self.init(__object: object, keyPath: keyPath, options: options, block: { (object, change, kvo) in
block(unsafeDowncast(object),
Change(rawDict: change), kvo)
})
}
At block(unsafeDowncast(object)
the compiler gives missing argument to:
.
According to Swift Evolution unsafeDowncast
got changed to:
The passed argument object
is x:
, but how do I need to define the to:
value?
This is, what I have come up with:
public convenience init<Object: AnyObject>(object: Object,
keyPath: String,
options: NSKeyValueObservingOptions = [],
block: @escaping (_ object: Object, _ change: Change, _ kvo: KVObserver) -> Void) {
self.init(__object: object, keyPath: keyPath, options: options, block: { (object, change, kvo) in
block(unsafeDowncast(object as AnyObject, to: Object.self),
Change(rawDict: change as [String : AnyObject]?), kvo)
})
}
The idea was, that to:
expects the .Type
and the block says, that object: Object
. That's why to: Object.self
.
change as [String : AnyObject]?)
was a compiler suggestion.
The compiler is satisfied. But I'm not sure if my code is correct. I'm not even 100% sure what the unsafeDowncast
does in the first place. What does to: T.Type
means in that context and how do I need to define it correctly?
Help is very appreciated.
unsafeDowncast(x, to: Object.self)
assumes that x
is an instance of Object
and returns x as Object
.
So it is identical to
x as! Object
but without any runtime checks. If x
is not an instance of Object
then the behaviour is undefined and anything can happen, from crashes
to unexpected results.
In Swift 2, the "destination type" was inferred from the context, in Swift 3 you have to provide it as an argument.
In your case, when the notification center calls the closure
{ (object, change, kvo) in
// ...
}
and you forward the call to the
block: (_ object: Object, _ change: Change, _ kvo: KVObserver) -> Void
closure given in the init method, you know that object
is an instance
of Object
, because that is what you passed in to the registration.
But the compiler does not know that. Therefore object
must be downcasted to Object
, and that is what
unsafeDowncast(object as AnyObject, to: Object.self)
does. The additional cast as AnyObject
is necessary because in
the notification callback, object
has type Any
, and not AnyObject
as in Swift 2. So your code
block(unsafeDowncast(object as AnyObject, to: Object.self), ...)
is correct, but the more straightforward way would be
block(object as! Object, ...)
The forced cast is acceptable here because you know that it must succeed, otherwise you would have a programming error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With