如何使用泛型类型来获取具有相同类型的对象

时间:2022-04-05 16:28:04

I have extension for NSManagedObject that should help me to transfer objects between contexts:

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

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:

现在它返回NSManagedObject的对象,我应该把它转发给我想要的类,如下所示:

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?

有没有办法在Swift中避免无用的转换,如果我从类MyEntity的对象调用transferTo(context:...)使它返回MyEntity的类型?

3 个解决方案

#1


2  

Update: For a better solution, see Rob's answer.

更新:有关更好的解决方案,请参阅Rob的回答。


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:

与在如何在NSManagedObject Swift扩展中创建托管对象子类的实例类似,可以使用通用帮助器方法完成:

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.

请注意,我已将返回类型更改为Self。 objectWithID()不返回可选项(与objectRegisteredForID()相反,因此无需在此处返回可选项。

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.

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

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.)

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

#2


2  

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.

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

Update: Martin R's answer points out that you can't cast the obtained object right away. I'd then do something like this:

更新:Martin R的答案指出你无法立即投射获得的对象。然后我会做这样的事情:

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

}

#3


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.

我很长一段时间都喜欢马丁的解决方案,但最近我遇到了麻烦。如果对象已被观察到KVO,那么这将崩溃。在这种情况下,Self是KVO子类,而objectWithID的结果不是那个子类,所以你会遇到像“无法将类型'myapp.Thing'(0xdeadbeef)的值转换为'myapp.Thing'(0xfdfdfdfd)的崩溃)“。有两个类称自己为myapp.Thing,as!使用实际的类对象。所以斯威夫特并没有被KVO课程的高贵谎言所迷惑。

The solution is to replace Self with a static type parameter by moving this to the context:

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

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.

T纯粹是在编译时定义的,所以即使object秘密地是T的子类,这也可以工作。

#1


2  

Update: For a better solution, see Rob's answer.

更新:有关更好的解决方案,请参阅Rob的回答。


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:

与在如何在NSManagedObject Swift扩展中创建托管对象子类的实例类似,可以使用通用帮助器方法完成:

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.

请注意,我已将返回类型更改为Self。 objectWithID()不返回可选项(与objectRegisteredForID()相反,因此无需在此处返回可选项。

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.

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

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.)

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

#2


2  

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.

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

Update: Martin R's answer points out that you can't cast the obtained object right away. I'd then do something like this:

更新:Martin R的答案指出你无法立即投射获得的对象。然后我会做这样的事情:

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

}

#3


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.

我很长一段时间都喜欢马丁的解决方案,但最近我遇到了麻烦。如果对象已被观察到KVO,那么这将崩溃。在这种情况下,Self是KVO子类,而objectWithID的结果不是那个子类,所以你会遇到像“无法将类型'myapp.Thing'(0xdeadbeef)的值转换为'myapp.Thing'(0xfdfdfdfd)的崩溃)“。有两个类称自己为myapp.Thing,as!使用实际的类对象。所以斯威夫特并没有被KVO课程的高贵谎言所迷惑。

The solution is to replace Self with a static type parameter by moving this to the context:

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

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.

T纯粹是在编译时定义的,所以即使object秘密地是T的子类,这也可以工作。