How to use generic types to get object with same type

前端 未结 3 1737
南笙
南笙 2020-12-06 13:24

I have extension for NSManagedObject that should help me to transfer objects between contexts:

extension NSManagedObject {

    func transferTo(         


        
相关标签:
3条回答
  • 2020-12-06 13:47

    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.)

    0 讨论(0)
  • 2020-12-06 14:01

    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.

    0 讨论(0)
  • 2020-12-06 14:08

    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)
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题