【问题标题】:How to use generic types to get object with same type如何使用泛型类型来获取具有相同类型的对象
【发布时间】:2015-01-30 09:58:23
【问题描述】:

我有 NSManagedObject 的扩展名,应该可以帮助我在上下文之间传输对象:

extension NSManagedObject {

    func transferTo(#context: NSManagedObjectContext) -> NSManagedObject? {

        return context.objectWithID(objectID)
    }

}

现在它返回NSManagedObject 的对象,我应该将它转换为我想要的类,如下所示:

let someEntity: MyEntity = // ...create someEntity
let entity: MyEntity = someEntity.transferTo(context: newContext) as? MyEntity

Swift 中有没有办法避免这种无用的强制转换,如果我从 MyEntity 类的对象调用 transferTo(context: ...) 使其返回类型为 MyEntity

【问题讨论】:

    标签: swift generics swift-extensions


    【解决方案1】:

    我很喜欢 Martin 的解决方案,但最近遇到了麻烦。如果对象已经被 KVO 观察到,那么这将崩溃。 Self 在这种情况下是 KVO 子类,objectWithID 的结果不是那个子类,所以你会遇到类似“无法将 'myapp.Thing' (0xdeadbeef) 类型的值转换为 'myapp.东西'(0xfdfdfdfd)。”有两个类自称为myapp.Thingas! 使用实际的类对象。所以 Swift 不会被 KVO 类的高贵谎言所迷惑。

    解决方案是将 Self 替换为静态类型参数,方法是将其移动到上下文中:

    extension NSManagedObjectContext {
        func transferredObject<T: NSManagedObject>(object: T) -> T {
            return objectWithID(object.objectID) as! T
        }
    }
    

    T 纯粹是在编译时定义的,所以即使objectT 的子类,它仍然有效。

    【讨论】:

    【解决方案2】:

    更新:如需更好的解决方案,请参阅Rob's answer


    类似于How can I create instances of managed object subclasses in a NSManagedObject Swift extension?, 这可以通过一个通用的辅助方法来完成:

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

    请注意,我已将返回类型更改为SelfobjectWithID() 确实 not 返回一个可选的 (相对于objectRegisteredForID(),所以不需要 在这里返回一个可选的。

    更新: Jean-Philippe Pellet's suggested 定义一个全局可重用函数而不是辅助方法 将返回值转换为适当的类型。

    我建议定义两个(重载)版本,以使 使用可选和非可选对象(没有不需要的 自动包装成一个可选的):

    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?
        }
    }
    

    (我已经更新了 Swift 2/Xcode 7 的代码。之前的代码 Swift 版本可以在编辑历史中找到。)

    【讨论】:

    • 有趣!这可能会保证使用这样的全局函数,而不是在需要时重新创建辅助方法? func cast&lt;T&gt;(obj: Any, type: T.Type) -&gt; T? { return obj as? T }我已经更新了我的答案以提出它。
    • @Jean-PhilippePellet: 好主意,但obj 的参数可能应该是Any?,否则在转换选项时似乎无法正常工作,例如@ 返回的NSManagedObject? 987654333@.
    • 哦,是的,你是对的,当然。在 Scala 中,我仍然想太多,非可选项不会自动包装。
    • 在使用了一段时间后,我发现了一个有趣的问题。如果你用它来传输一个已经被 KVO 观察到的对象,它就会崩溃。 Self 在这种情况下是 KVO 的子类,objectWithID 的结果是它的超类。因此,您会收到类似“无法将 'myapp.Thing' (0xdeadbeef) 类型的值转换为 'myapp.Thing' (0xfdfdfdfd)”这样的错误。仍在研究一个优雅的解决方案。
    • 找到解决方案;添加为答案。
    【解决方案3】:

    这样就可以了:

    func transferTo(#context: NSManagedObjectContext) -> Self?
    

    在调用站点,Self 解析为您正在调用此方法的对象的静态已知类型。当您不知道符合协议但仍想引用它的最终类型时,这在协议中使用也特别方便。

    更新: Martin R's answer 指出你不能立即投射获得的对象。然后我会做这样的事情:

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

    【讨论】:

    • 这是我首先尝试的,但是您必须以某种方式将返回值从context.objectWithID(objectID)(即NSManagedObject)转换为实际的返回类型,并且as? Self 或类似的不会编译(或者我无法弄清楚如何)。
    • 现在我们的答案之间有一个保留循环 :) 对于我们已经 知道 对象属于所需类型的这种特殊情况,unsafeBitCast() 将作为好吧。
    • 很好!但是 IMO,参数应该是 AnyObject 有两个原因。 1)Any占用32bit内存,类实例转换为Any会消耗CPU和内存。 2) 与Any不同,我们不能将Optional传递给AnyObject参数,它比Any更安全。
    • 在我看来实际上需要两个函数:func cast&lt;T&gt;(obj: AnyObject?, type: T.Type) -&gt; T?func cast&lt;T&gt;(obj: AnyObject, type: T.Type) -&gt; T
    • 更正:“Any占用32bit”→“Any占用32字节
    猜你喜欢
    • 2018-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-05
    相关资源
    最近更新 更多