How can I cast from UnsafeMutablePointer<Void> to Concrete Object Type
I've created a KVO observer and passed a custom class as context just like this
class Info : NSObject
{
}
class Foo : NSObject {
dynamic var property = ""
}
var info = Info()
class Observing : NSObject {
func observe1(object: NSObject) {
object.addObserver(self, forKeyPath: "property", options: .New, context: &info)
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
println("observed \(keyPath) change:'\(change)' context: \(context)")
}
}
let o = Observing()
var object = Foo()
o.observe1(object)
object.property = "new value"
I would like to know how to cast context back to Info class
UnsafeMutablePointer has an initializer that takes another UnsafeMutablePointer of another type, the result being the same pointer but to the new type.
So in your case:
let infoPtr = UnsafeMutablePointer<Info>(context)
let info = infoPtr.memory
Beware though this is, as the docs describe it, "a fundamentally unsafe conversion". If the pointer you have is not a pointer to the type you convert it to, or if the memory you are now accessing it is not valid in this context, you may end up accessing invalid memory and your program will get a little crashy.
Related
In the past we've used Objective-C to anonymously get the sharedInstance of a class this way:
+ (nullable NSObject *)sharedInstanceForClass:(nonnull Class)aClass
{
// sharedPropertyProvider
NSObject<KVASharedPropertyProvider> *sharedPropertyProvider = [aClass conformsToProtocol:#protocol(KVASharedPropertyProvider)]
? (NSObject<KVASharedPropertyProvider> *)aClass
: nil;
if (sharedPropertyProvider == nil)
{
return nil;
}
// return
return [sharedPropertyProvider.class sharedInstance];
}
It's protocol based. We put this protocol on every class we have with a shared instance where we need to do this.
#objc (KVASharedPropertyProvider)
public protocol KVASharedPropertyProvider: AnyObject
{
#objc (sharedInstance)
static var sharedInstance: AnyObject { get }
}
The above works fine in Objective-C (and when called from Swift). When attempting to write the same equivalent code in Swift, however, there appears to be no way to do it. If you take this specific line(s) of Objective-C code:
NSObject<KVASharedPropertyProvider> *sharedPropertyProvider = [aClass conformsToProtocol:#protocol(KVASharedPropertyProvider)]
? (NSObject<KVASharedPropertyProvider> *)aClass
: nil;
And attempt to convert it to what should be this line of Swift:
let sharedPropertyProvider = aClass as? KVASharedPropertyProvider
... initially it appears to succeed. The compiler just warns you that sharedPropertyProvider isn't be used. But as soon as you attempt to use it like so:
let sharedInstance = sharedPropertyProvider?.sharedInstance
It gives you the compiler warning back on the previous line where you did the cast:
Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type
'KVASharedPropertyProvider' always fails
Any ideas? Is Swift simply not capable of casting AnyClass to a protocol in the same way that it could be in Objective-C?
In case you're wondering why we need to do this, it's because we have multiple xcframeworks that need to operate independently, and one xcframework (a core module) needs to optionally get the shared instance of a higher level framework to provide special processing if present (i.e. if installed) but that processing must be initiated from the lower level.
Edit:
It was asked what this code looked like in Swift (which does not work). It looks like this:
static func shared(forClass aClass: AnyClass) -> AnyObject?
{
guard let sharedPropertyProvider = aClass as? KVASharedPropertyProvider else
{
return nil
}
return type(of: sharedPropertyProvider).sharedInstance
}
The above generates the warning:
Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type
'KVASharedPropertyProvider' always fails
It was suggested I may need to use KVASharedPropertyProvider.Protocol. That looks like this:
static func shared(forClass aClass: AnyClass) -> AnyObject?
{
guard let sharedPropertyProvider = aClass as? KVASharedPropertyProvider.Protocol else
{
return nil
}
return type(of: sharedPropertyProvider).sharedInstance
}
And that generates the warning:
Cast from 'AnyClass' (aka 'AnyObject.Type') to unrelated type
'KVASharedPropertyProvider.Protocol' always fails
So, I assume you have something like this
protocol SharedProvider {
static var shared: AnyObject { get }
}
class MySharedProvider: SharedProvider {
static var shared: AnyObject = MySharedProvider()
}
If you want to use AnyObject/AnyClass
func sharedInstanceForClass(_ aClass: AnyClass) -> AnyObject? {
return (aClass as? SharedProvider.Type)?.shared
}
Better approach
func sharedInstanceForClass<T: SharedProvider>(_ aClass: T.Type) -> AnyObject {
return T.shared
}
Perhaps this question is more general than I will make it seem, but I wanted to make sure I showed my full context in case something there is the cause of this issue.
I wrote a singleton class with a KVC-compliant property and two methods:
class Singleton: NSObject {
static let sharedInstance = Singleton()
#objc dynamic var aProperty = false
func updateDoesntWork() {
aProperty = !aProperty
}
func updateDoesWork() {
Singleton.sharedInstance.aProperty = !aProperty
}
}
I add an observer for the property in my app delegate's setup code:
Singleton.sharedInstance.addObserver(self, forKeyPath: #keyPath(Singleton.aProperty), options: [.new], context: nil)
I override my app delegate's observeValue() method:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
NSLog("observeValue(forKeyPath: \(String(describing:keyPath)), of: \(String(describing:object)), change: \(String(describing:change)), context:\(String(describing:context)))")
}
Now, if I call Singleton.sharedInstance.updateDoesntWork(), I don't get a log entry for the change in aProperty. The property is changed (I verified this in the debugger), it's just that no notification is sent.
Whereas, if I call Singleton.sharedInstance.updateDoesWork(), everything works as I would expect -- the property is also changed, of course, but most importantly, this time the observer is notified of the change (the log entry is printed).
It makes no sense to me that I should need the full Singleton.sharedInstance.aProperty rather than just aProperty for KVO to work. What am I missing?
I assume you have trouble to use "var" for a singleton. You may consider use the following snippet to create a singleton and initializate some values including the observation exclusively used by the singleton:
class Singleton: NSObject {
static private var sharedInstanceObserver : NSKeyValueObservation!
static let sharedInstance: Singleton = {
let sInstance = Singleton()
sharedInstanceObserver = sInstance.observe(\Singleton.aProperty, options: .new) { st, value in
print(st, value)
}
return sInstance
}()
#objc dynamic var aProperty = false
func updateDoesntWork() {
aProperty = !aProperty
}
func updateDoesWork() {
Singleton.sharedInstance.aProperty = !aProperty
}
}
I have a simple Result object which should contain an object (class, struct or enum) and a Bool to say whether it was cancelled or not. I need to interrogate this object along its path (before it gets to its destination, where the destination knows what kind of object to expect) to determine whether it was cancelled or not (without worrying about the accompanying object at that moment). My object looks like:
import Foundation
#objc protocol Resultable {
var didCancel: Bool { get }
}
class Result<T>: Resultable {
let didCancel: Bool
let object: T?
init(didCancel: Bool, object: T?) {
self.didCancel = didCancel
self.object = object
}
}
The idea being that my Result object can wrap the didCancel flag and the actual object (which can be of any type), and the fact that it implements the Resultable protocol means that I can interrogate it at any point to see whether it was cancelled by casting it to Resultable.
I understand (while not liking it) that the protocol has to be prefixed with #objc so that we can cast to it (according to the Apple docs). Unfortunately, when I run the code below (in a playground or in a project), I get a nasty "does not implement methodSignatureForSelector:" error message:
let test = Result(didCancel: false, object: NSArray())
println(test.didCancel)
// transform the object into the form it will be received in
let anyTest: AnyObject = test
if let castTest = anyTest as? Resultable {
println(castTest.didCancel)
}
It seems that despite the protocol being prefixed with #objc, it also wants the actual class to inherit from NSObject (and this is not a requirement that Apple makes explicit). This is obviously a problem for a generic class.
Is there anything I am missing here? Any way to get this to work? Failing that, are there any workarounds (although I strongly believe that this kind of thing should be possible - perhaps we can hope that Apple will do away with the #objc protocol casting requirement at some stage)?
UPDATE
It looks like this is solved with Swift 1.2
You can now cast to a non-ObjC protocol, and without the object having to inherit from NSObject.
It seems that despite the protocol being prefixed with #objc, it also wants the actual class to inherit from NSObject
This is not true. For example, the following code works as expected:
#objc protocol MyProtocol {
var flag: Bool { get }
}
class MyClass: MyProtocol {
let flag = true
}
let foo:AnyObject = MyClass()
if let p = foo as? MyProtocol {
println(p.flag)
}
The problems is that: Any methods/properties declared in Swift Generic classes are invisible from Objective-C. Hence, from the perspective of #objc protocol Resultable, didCancel property declared in Result<T> is invisible. That's why methodSignatureForSelector: is called.
The workaround here is very annoying: You have to have non Generic base class for Result that implements didCancel.
#objc protocol Resultable {
var didCancel: Bool { get }
}
class ResultBase: Resultable {
let didCancel: Bool
init(didCancel: Bool) { self.didCancel = didCancel }
}
class Result<T>: ResultBase, Resultable {
// ^^^^^^^^^^^^
// You have to explicitly conforms `Resultable` here as well for some reason :/
let object: T?
init(didCancel: Bool, object: T?) {
self.object = object
super.init(didCancel: didCancel)
}
}
let test = Result(didCancel: false, object: NSArray())
let anyTest: AnyObject = test
if let castTest = anyTest as? Resultable {
println(castTest.didCancel) // -> outputs "false"
}
I have extension for NSManagedObject that should help me to transfer objects between contexts:
extension NSManagedObject {
func transferTo(#context: NSManagedObjectContext) -> NSManagedObject? {
return context.objectWithID(objectID)
}
}
for now it return object of NSManagedObject and i should cast it to class what i want, like this:
let someEntity: MyEntity = // ...create someEntity
let entity: MyEntity = someEntity.transferTo(context: newContext) as? MyEntity
Is there a way in Swift to avoid that useless casting and if i call transferTo(context: ...) from object of class MyEntity make it return type to MyEntity?
I've liked Martin's solution for a long time, but I recently ran into trouble with it. If the object has been KVO observed, then this will crash. Self in that case is the KVO subclass, and the result of objectWithID is not that subclass, so you'll get a crash like "Could not cast value of type 'myapp.Thing' (0xdeadbeef) to 'myapp.Thing' (0xfdfdfdfd)." There are two classes that call themselves myapp.Thing, and as! uses the actual class object. So Swift is not fooled by the noble lies of KVO classes.
The solution is to replace Self with a static type parameter by moving this to the context:
extension NSManagedObjectContext {
func transferredObject<T: NSManagedObject>(object: T) -> T {
return objectWithID(object.objectID) as! T
}
}
T is purely defined at compile-time, so this works even if object is secretly a subclass of T.
Update: For a better solution, see Rob's answer.
Similarly as in How can I create instances of managed object subclasses in a NSManagedObject Swift extension?,
this can be done with a generic helper method:
extension NSManagedObject {
func transferTo(context context: NSManagedObjectContext) -> Self {
return transferToHelper(context: context)
}
private func transferToHelper<T>(context context: NSManagedObjectContext) -> T {
return context.objectWithID(objectID) as! T
}
}
Note that I have changed the return type to Self.
objectWithID() does not return an optional
(in contrast to objectRegisteredForID(), so there is no need to
return an optional here.
Update: Jean-Philippe Pellet's suggested
to define a global reusable function instead of the helper method
to cast the return value to the appropriate type.
I would suggest to define two (overloaded) versions, to make this
work with both optional and non-optional objects (without an unwanted
automatic wrapping into an optional):
func objcast<T>(obj: AnyObject) -> T {
return obj as! T
}
func objcast<T>(obj: AnyObject?) -> T? {
return obj as! T?
}
extension NSManagedObject {
func transferTo(context context: NSManagedObjectContext) -> Self {
let result = context.objectWithID(objectID) // NSManagedObject
return objcast(result) // Self
}
func transferUsingRegisteredID(context context: NSManagedObjectContext) -> Self? {
let result = context.objectRegisteredForID(objectID) // NSManagedObject?
return objcast(result) // Self?
}
}
(I have updated the code for Swift 2/Xcode 7. The code for earlier
Swift versions can be found in the edit history.)
This will do the trick:
func transferTo(#context: NSManagedObjectContext) -> Self?
At call site, Self resolves to the statically known type of the object you're calling this method on. This is also especially handy to use in protocols when you don't know the final type that will conform to the protocol but still want to reference it.
Update: Martin R's answer points out that you can't cast the obtained object right away. I'd then do something like this:
// top-level utility function
func cast<T>(obj: Any?, type: T.Type) -> T? {
return obj as? T
}
extension NSManagedObject {
func transferTo(#context: NSManagedObjectContext) -> NSManagedObject? {
return cast(context.objectWithID(objectID), self.dynamicType)
}
}
I'm assembling a class which has several states, as defined by an enum, and a read-only property "state" which returns the instance's current state. I was hoping to use KVO techniques to observe changes in state but this doesn't seem possible:
dynamic var state:ItemState // Generates compile-time error: Property cannot be marked dynamic because its type cannot be represented in Objective-C
I guess I could represent each state as an Int or String, etc. but is there a simple alternative workaround that would preserve the type safety that the enum would otherwise provide?
Vince.
Perhaps this is only available in swift 2+, but you can make an enum property directly observable without having to refer to its rawValue. It does come with some limitations however.
have the containing class extend from NSObject (directly or indirectly)
mark the enum with #objc
extend the enum from Int
declare the property as dynamic.
class SomeModel : NSObject { // (1) extend from NSObject
#objc // (2) mark enum with #objc
enum ItemState : Int, CustomStringConvertible { // (3) extend enum from Int
case Ready, Set, Go
// implementing CustomStringConvertible for example output
var description : String {
switch self {
case .Ready: return "Ready"
case .Set: return "Set"
case .Go: return "Go"
}
}
}
dynamic var state = ItemState.Ready // (4) declare property as dynamic
}
Elsewhere:
class EnumObserverExample : NSObject {
private let _model : SomeModel
init(model:SomeModel) {
_model = model
super.init()
_model.addObserver(self, forKeyPath:"state", options: NSKeyValueObservingOptions.Initial, context: nil)
}
deinit {
_model.removeObserver(self, forKeyPath:"state", context: nil)
}
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if "state" == keyPath {
print("Observed state change to \(_model.state)")
}
}
}
let model = SomeModel()
let observer = EnumObserverExample(model:model)
model.state = .Set
model.state = .Go
Outputs:
Observed state change to Ready (because .Initial was specified)
Observed state change to Set
Observed state change to Go
I came across the same problem a while ago.
In the end I used an enum for the state and added an additional 'raw' property which is set by a property observer on the main state property.
You can KVO the 'raw' property but then reference the real enum property when it changes.
It's obviously a bit of a hack but for me it was better than ditching the enum altogether and losing all the benefits.
eg.
class Model : NSObject {
enum AnEnumType : String {
case STATE_A = "A"
case STATE_B = "B"
}
dynamic private(set) var enumTypeStateRaw : String?
var enumTypeState : AnEnumType? {
didSet {
enumTypeStateRaw = enumTypeState?.rawValue
}
}
}
ADDITIONAL:
If you are writing the classes that are doing the observing in Swift here's a handy utility class to take some of the pain away.
The benefits are:
no need for your observer to subclass NSObject.
observation callback code as a closure rather than having to implement
observeValueForKeyPath:BlahBlah...
no need to make sure you removeObserver, it's taken care of for you.
The utility class is called KVOObserver and an example usage is:
class ExampleObserver {
let model : Model
private var modelStateKvoObserver : KVOObserver?
init(model : Model) {
self.model = model
modelStateKvoObserver = KVOObserver.observe(model, keyPath: "enumTypeStateRaw") { [unowned self] in
println("new state = \(self.model.enumTypeState)")
}
}
}
Note [unowned self] in the capture list to avoid reference cycle.
Here's KVOObserver...
class KVOObserver: NSObject {
private let callback: ()->Void
private let observee: NSObject
private let keyPath: String
private init(observee: NSObject, keyPath : String, callback: ()->Void) {
self.callback = callback
self.observee = observee
self.keyPath = keyPath;
}
deinit {
println("KVOObserver deinit")
observee.removeObserver(self, forKeyPath: keyPath)
}
override func observeValueForKeyPath(keyPath: String,
ofObject object: AnyObject,
change: [NSObject : AnyObject],
context: UnsafeMutablePointer<()>) {
println("KVOObserver: observeValueForKey: \(keyPath), \(object)")
self.callback()
}
class func observe(object: NSObject, keyPath : String, callback: ()->Void) -> KVOObserver {
let kvoObserver = KVOObserver(observee: object, keyPath: keyPath, callback: callback)
object.addObserver(kvoObserver, forKeyPath: keyPath, options: NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Initial, context: nil)
return kvoObserver
}
}