Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
898 views
in Technique[技术] by (71.8m points)

core data - Using Swift protocols with generics

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
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...