问题
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
?
回答1:
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.)
回答2:
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
.
回答3:
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)
}
}
来源:https://stackoverflow.com/questions/28233302/how-to-use-generic-types-to-get-object-with-same-type