【问题标题】:How can I create instances of managed object subclasses in a NSManagedObject Swift extension?如何在 NSManagedObject Swift 扩展中创建托管对象子类的实例?
【发布时间】:2015-01-22 10:14:35
【问题描述】:

当创建NSManagedObject 的扩展助手以创建新的托管对象子类时,swift 提供了Self 类型来模仿instancetype,这很棒,但我似乎无法从AnyObject 进行类型转换。以下代码无法编译并出现错误 'AnyObject' is not convertible to '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)
    }

}

【问题讨论】:

    标签: swift core-data nsmanagedobject


    【解决方案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¢:通用函数是 private 关键字的一个很好的用例。
    • @JonathanZhan:你说得对,我会更新它。谢谢。
    • @ambientlight:你完全正确。我在下面单独发布的“替代方法”中使用了它。
    • @MartinR 很好的解释。但我不明白为什么 func createInContext(context:NSManagedObjectContext, type : T.Type) -> T 需要'type'参数?不能通过调用函数的返回类型来推断“T”的类型吗? #newToSwift :)
    • @KickimusButticus:你说得对,这也适用。但请注意,存在更简单的解决方案,请参阅下面的stackoverflow.com/a/33583941/1187415
    【解决方案2】:

    这是解决问题的另一种方法,通过实施 initializer 方法(用 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 类型的对象。


    Swift 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)
        }
    }
    

    【讨论】:

    • 在 ObjC 中定义 YourEntity 时似乎不起作用 - 测试中未调用扩展中的 init - 在使用 Swift 时完美运行
    • 实际上,如果方法参数与重写的构造函数中的参数不完全相同,它可以与在 ObjC 中实现的 YourEntity 一起使用 - 例如convenience init(usedContext: NSManagedObjectContext) 定义和调用 let entity = YourEntity(usedContext: context) 工作得很好
    • @KrzysztofSkrzynecki:感谢您的反馈! – init(context:) 构造函数是在 iOS 10 中引入的,当我写这个答案的初始版本时它并不存在。
    【解决方案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(…) 函数将返回正确的类型,无需进一步类型转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      相关资源
      最近更新 更多