Using Swift protocols with generics

只谈情不闲聊 提交于 2019-11-27 02:28:23

问题


I have a simple example that seems like it should work:

import CoreData

@objc protocol CoreDataModel {
    @optional class func entityName() -> String
}

class AbstractModel: NSManagedObject, CoreDataModel {
    class func create<T : CoreDataModel>(context:NSManagedObjectContext) -> T {
        var name = T.entityName?()
        var object = NSEntityDescription.insertNewObjectForEntityForName(name, inManagedObjectContext: context) as T
        return object
    }
}

So we have a class called AbstractModel which conforms to the protocol CoreDataModel, and CoreDataModel defines an optional class method called entityName.

However, this line:

var name = T.entityName?()

causes the error:

Expected member name or constructor call after type name

Any idea what I'm doing wrong?

Edit

Removing the word @optional from the declaration and changing the function a bit allows the code to compile, but now I get a runtime error saying that the

'Swift dynamic cast failed'

@objc protocol CoreDataModel {
     class func entityName() -> String
}

class AbstractModel: NSManagedObject, CoreDataModel {

    class func entityName() -> String {
        return "AbstractModel"
    }

    class func create<T : CoreDataModel>(aClass:T.Type, context:NSManagedObjectContext) -> T {
        var name = aClass.entityName()
        var object = NSEntityDescription.insertNewObjectForEntityForName(name, inManagedObjectContext: context) as T
        return object
    }
}

回答1:


I cannot explain why your code causes a runtime exception. But it works if you change the function prototype

class func create<T : CoreDataModel>(aClass:T.Type, context:NSManagedObjectContext) -> T 

to

class func create<T : NSManagedObject where T: CoreDataModel>(aClass:T.Type, context:NSManagedObjectContext) -> T

Assuming that your managed object subclass conforms to the protocol, for example

extension Event : CoreDataModel {
    class func entityName() -> String {
        return "Event"
    }
}

then this works and creates a new object:

let newManagedObject = AbstractModel.create(Event.self, context: context)

Alternatively, you could use the approach from the answer to "Swift: return Array of type self" and define an extension to the NSManagedObjectContext class:

extension NSManagedObjectContext {
    func create<T : NSManagedObject where T : CoreDataModel >(entity: T.Type) -> T {
        var classname = entity.entityName()
        var object = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: self) as T
        return object
    }
}

Then a new object would be created as

let newManagedObject = context.create(Event.self)



回答2:


From "The Swift Programming Language"

Because T is a placeholder, Swift does not look for an actual type called T.

As T is not a real type, it is maybe not useful to cast to T.



来源:https://stackoverflow.com/questions/24834753/using-swift-protocols-with-generics

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!