Как создать экземпляры подклассов управляемого объекта в расширении NSManagedObject Swift?
При создании вспомогательного помощника расширения для NSManagedObject
для создания нового подкласса управляемого объекта, swift предоставляет тип Self
для mimic instancetype
, который является большим, но я не могу показаться typecast из AnyObject
. Следующий код не компилируется с ошибкой "AnyObject" не конвертируется в "Self"
Справка
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)
}
}
Ответы
Ответ 1
(Обновлено для Swift 3/4. Решения для более ранних версий Swift можно найти в истории редактирования.)
Вы можете использовать unsafeDowncast
для приведения возвращаемого значения NSEntityDescription.insertNewObject()
к Self
(это тип, для которого метод фактически вызывается):
extension NSManagedObject {
class func create(in context: NSManagedObjectContext) -> Self {
let classname = entityName()
let object = NSEntityDescription.insertNewObject(forEntityName: classname, into: context)
return unsafeDowncast(object, to: self)
}
// Returns the unqualified class name, i.e. the last component.
// Can be overridden in a subclass.
class func entityName() -> String {
return String(describing: self)
}
}
затем
let obj = YourEntity.createInContext(context)
работает, и компилятор правильно определяет тип объекта obj
как YourEntity
.
Ответ 2
Вот другой подход к решению проблемы, реализующий метод инициализатора (протестированный с 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)
}
}
Методы init имеют неявный возвращаемый тип Self
и не требуется никаких приемов приведения.
let obj = YourEntity(context: context)
создает объект типа YourEntity
.
Свифт 3/4 обновление:
extension NSManagedObject {
// Returns the unqualified class name, i.e. the last component.
// Can be overridden in a subclass.
class func entityName() -> String {
return String(describing: self)
}
convenience init(context: NSManagedObjectContext) {
let eName = type(of: self).entityName()
let entity = NSEntityDescription.entity(forEntityName: eName, in: context)!
self.init(entity: entity, insertInto: context)
}
}
Ответ 3
В Swift 2 есть очень умное решение, использующее протокол и расширение протокола
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
}
}
В каждом подклассе NSManagedObject
добавьте протокол Fetchable
и реализуйте свойство entityName
.
Теперь функция MyEntity.createInContext(…)
вернет правильный тип без последующего литья типов.