query - swift 4 core data tutorial
¿Cómo puedo crear instancias de subclases de objetos gestionados en una extensión NSManagedObject Swift? (3)
Al crear un asistente de extensión para NSManagedObject
para crear una nueva subclase de objeto gestionado, swift proporciona el tipo de Self
para imitar el tipo de instancetype
que es excelente, pero parece que no puedo convertirlo desde AnyObject
. El código siguiente no se compila con el error ''AnyObject'' no es convertible a ''Self''
¿Ayuda?
extension NSManagedObject
{
class func createInContext(context:NSManagedObjectContext) -> Self {
var classname = className()
var object: AnyObject = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: context)
return object
}
class func className() -> String {
let classString = NSStringFromClass(self)
//Remove Swift module name
let range = classString.rangeOfString(".", options: NSStringCompareOptions.CaseInsensitiveSearch, range: Range<String.Index>(start:classString.startIndex, end: classString.endIndex), locale: nil)
return classString.substringFromIndex(range!.endIndex)
}
}
Aquí hay un enfoque diferente para resolver el problema, mediante la implementación de un método de inicialización (probado con Xcode 7.1):
extension NSManagedObject {
// Returns the unqualified class name, i.e. the last component.
// Can be overridden in a subclass.
class func entityName() -> String {
return String(self)
}
convenience init(context: NSManagedObjectContext) {
let eName = self.dynamicType.entityName()
let entity = NSEntityDescription.entityForName(eName, inManagedObjectContext: context)!
self.init(entity: entity, insertIntoManagedObjectContext: context)
}
}
Los métodos Init tienen un tipo de retorno implícito de Self
y no se necesitan trucos de lanzamiento.
let obj = YourEntity(context: context)
crea un objeto del tipo YourEntity
.
En Swift 2 hay una solución muy inteligente que utiliza un protocolo y una extensión de protocolo
protocol Fetchable
{
typealias FetchableType: NSManagedObject
static var entityName : String { get }
static func createInContext(context: NSManagedObjectContext) -> FetchableType
}
extension Fetchable where Self : NSManagedObject, FetchableType == Self
{
static func createInContext(context: NSManagedObjectContext) -> FetchableType
{
return NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext: context) as! FetchableType
}
}
En cada subclase NSManagedObject
agregue el protocolo Fetchable
e implemente la propiedad entityName
.
Ahora la función MyEntity.createInContext(…)
devolverá el tipo correcto sin más conversión de tipo.
El truco es usar un método de ayuda genérico que infiere el tipo de self
del contexto. Su método className()
también se puede simplificar un poco, y un nombre mejor podría ser por entityName()
:
extension NSManagedObject
{
class func createInContext(context:NSManagedObjectContext) -> Self {
return createInContextHelper(context)
}
private class func createInContextHelper<T>(context:NSManagedObjectContext) -> T {
let classname = entityName()
let object = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: context) as! T
return object
}
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = split(classString, { $0 == "." })
return components.last ?? classString
}
}
Entonces
let obj = YourEntity.createInContext(context)
funciona y el compilador infiere el tipo de obj
correctamente como YourEntity
.
Actualización: utilizando las ideas de Cómo usar los tipos genéricos para obtener objetos del mismo tipo , esto también se puede hacer con una función reutilizable global en lugar del método auxiliar para convertir el valor de retorno al tipo apropiado:
func objcast<T>(obj: AnyObject) -> T {
return obj as T
}
extension NSManagedObject
{
class func createInContext(context:NSManagedObjectContext) -> Self {
let classname = entityName()
let object: AnyObject = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: context)
return objcast(object)
}
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = split(classString, { $0 == "." })
return components.last ?? classString
}
}
Actualización para Swift 1.2 / Xcode 6.3 y Swift 2 / Xcode 7:
func objcast<T>(obj: AnyObject) -> T {
return obj as! T
}
extension NSManagedObject
{
class func createInContext(context:NSManagedObjectContext) -> Self {
let classname = entityName()
let object: AnyObject = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: context)
return objcast(object)
}
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = classString.componentsSeparatedByString(".")
return components.last ?? classString
}
}
O con unsafeBitCast
lugar de un método de ayuda, como se sugiere en los comentarios:
extension NSManagedObject
{
class func createInContext(context:NSManagedObjectContext) -> Self {
let classname = entityName()
let object: AnyObject = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: context)
return unsafeBitCast(object, self)
}
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = classString.componentsSeparatedByString(".")
return components.last ?? classString
}
}