代码之家  ›  专栏  ›  技术社区  ›  mfaani

当泛型类型通过参数传递时,Swift无法推断泛型类型

  •  6
  • mfaani  · 技术社区  · 6 年前

    我正在为核心数据编写一个通用包装器类。

    以下是我的一些基本类型。没什么特别的。

    typealias CoreDataSuccessLoad = (_: NSManagedObject) -> Void
    typealias CoreDataFailureLoad = (_: CoreDataResponseError?) -> Void
    typealias ID = String
    
    
    enum CoreDataResult<Value> {
        case success(Value)
        case failure(Error)
    }
    
    enum CoreDataResponseError : Error {
        typealias Minute = Int
        typealias Key = String
        case idDoesNotExist
        case keyDoesNotExist(key: Key)
        case fetch(entityName: String)
    }
    

    我已经在协议中抽象了我的coredata写入。如果您能让我知道您对我试图实现的抽象的评论,我将不胜感激。 但在扩展中,我遇到了以下错误:

    应为参数类型“NSFetchRequest<_>'

    protocol CoreDataWriteManagerProtocol {
        associatedtype ManagedObject : NSManagedObject
    
        var persistentContainer : NSPersistentContainer {get}
        var idName : String {get}
        func loadFromDB(storableClass : ManagedObject.Type, id: ID) throws -> CoreDataResult<ManagedObject>
        func update(storableClass : ManagedObject.Type, id: ID, fields: [String : Any]) throws
        func fetch(request: NSFetchRequest<ManagedObject>, from context: NSManagedObjectContext)
        init(persistentContainer : NSPersistentContainer)
    }
    
    extension CoreDataWriteManagerProtocol {
        private func loadFromDB(storableClass : ManagedObject.Type, id: ID) -> CoreDataResult<ManagedObject>{
            let predicate = NSPredicate(format: "%@ == %@", idName, id)
    
            let fetchRequest : NSFetchRequest = storableClass.fetchRequest()
            fetchRequest.predicate = predicate
    
            // ERROR at below line!
            return fetch(request: fetchRequest, from: persistentContainer.viewContext) 
        }
    
        func fetch<ManagedObject: NSManagedObject>(request: NSFetchRequest<ManagedObject>, from context: NSManagedObjectContext) -> CoreDataResult<ManagedObject>{
            guard let results = try? context.fetch(request) else {
                return .failure(CoreDataResponseError.fetch(entityName: request.entityName ?? "Empty Entity Name")) // @TODO not sure if entityName gets passed or not.
            }
            if let result = results.first {
                return .success(result)
            }else{
                return .failure(CoreDataResponseError.idDoesNotExist)
            }
        }
    }
    

    另外,如果我更改行:

    let fetchRequest : NSFetchRequest = storableClass.fetchRequest()
    

    let fetchRequest : NSFetchRequest<storableClass> = storableClass.fetchRequest()
    

    我得到以下错误:

    使用未声明的类型“storableClass”`

    我的直觉告诉我,编译器无法映射“类型参数”,即它不理解这一点 storableClass 实际上是一种类型。相反,它只能映射泛型参数或实际类型。因此,这不起作用。

    编辑:

    我使用了静态方法瓦迪安,并写了以下内容:

    private func create(_ entityName: String, json : [String : Any]) throws -> ManagedObject {
    
        guard let entityDescription = NSEntityDescription.entity(forEntityName: entityName, in: Self.persistentContainer.viewContext) else {
            print("entityName: \(entityName) doesn't exist!")
            throw CoreDataError.entityNotDeclared(name: entityName)
        }
    
        let _ = entityDescription.relationships(forDestination: NSEntityDescription.entity(forEntityName: "CountryEntity", in: Self.persistentContainer.viewContext)!)
        let relationshipsByName = entityDescription.relationshipsByName
    
        let propertiesByName = entityDescription.propertiesByName
    
        guard let managedObj = NSEntityDescription.insertNewObject(forEntityName: entityName, into: Self.persistentContainer.viewContext) as? ManagedObject else {
            throw CoreDataError.entityNotDeclared(name: entityName)
        }
    
        for (propertyName,_) in propertiesByName {
            if let value = json[propertyName] {
                managedObj.setValue(value, forKey: propertyName)
            }
        }
        // set all the relationships
        guard !relationshipsByName.isEmpty else {
            return managedObj
        }
    
        for (relationshipName, _ ) in relationshipsByName {
            if let object = json[relationshipName], let objectDict = object as? [String : Any] {
                let entity = try create(relationshipName, json: objectDict)
                managedObj.setValue(entity, forKey: relationshipName)
            }
        }
        return managedObj
    }
    

    但是下面这一段并不是通用的,因为我用 as? ManagedObject . 基本上并不像瓦迪安说的那么快:

    guard let managedObj = NSEntityDescription.insertNewObject(forEntityName: entityName, into: Self.persistentContainer.viewContext) as? ManagedObject else {
        throw CoreDataError.entityNotDeclared(name: entityName)
    }
    

    有什么办法吗?

    2 回复  |  直到 6 年前
        1
  •  7
  •   vadian    6 年前

    我的建议有点不同。它使用 方法

    呼叫 loadFromDB fetch NSManagedObject 子类。这样做的好处是,始终返回关联的类型,而无需进行任何进一步的类型转换。

    另一个变化是 throw 不断重复的错误。作为核心数据,API广泛依赖于 我的建议是放弃 CoreDataResult<Value> . 所有错误都被传递。成功时返回对象,失败时抛出错误。

    我漏掉了那封信 id update static func predicate(for id : ID)

    protocol CoreDataWriteManagerProtocol {
        associatedtype ManagedObject : NSManagedObject = Self
    
        static var persistentContainer : NSPersistentContainer { get }
        static var entityName : String { get }
        static func loadFromDB(predicate: NSPredicate?) throws -> ManagedObject
        static func fetch(request: NSFetchRequest<ManagedObject>) throws -> ManagedObject
        static func insertNewObject() -> ManagedObject
    }
    
    extension CoreDataWriteManagerProtocol where Self : NSManagedObject {
    
        static var persistentContainer : NSPersistentContainer {
            return (UIApplication.delegate as! AppDelegate).persistentContainer
        }
    
        static var entityName : String {
            return String(describing:self)
        }
    
        static func loadFromDB(predicate: NSPredicate?) throws -> ManagedObject {
            let request = NSFetchRequest<ManagedObject>(entityName: entityName)
            request.predicate = predicate
            return try fetch(request: request)
        }
    
        static func fetch(request: NSFetchRequest<ManagedObject>) throws -> ManagedObject {
            guard let results = try? persistentContainer.viewContext.fetch(request) else {
                throw CoreDataResponseError.fetch(entityName: entityName)
            }
            if let result = results.first {
                return result
            } else {
                throw CoreDataResponseError.idDoesNotExist
            }
        }
    
        static func insertNewObject() -> ManagedObject {
            return NSEntityDescription.insertNewObject(forEntityName: entityName, into: persistentContainer.viewContext) as! ManagedObject
        }
    }
    
        2
  •  4
  •   Dávid Pásztor    6 年前

    问题是 NSManagedObject.fetchRequest() 返回类型为 NSFetchRequest<NSFetchRequestResult> ,这是非泛型的。您需要更新您的应用程序的定义 fetch 函数来解释这一点。顺便说一句,协议扩展中默认实现的函数签名实际上与协议定义中的函数签名不匹配,因此这些函数签名也需要更新。

    您还需要更改的实现 fetch(request:,from:) 自从 NSManagedObjectContext.fetch() [Any] ,所以您需要将其转换为 [ManagedObject] 取来

    protocol CoreDataWriteManagerProtocol {
        associatedtype ManagedObject : NSManagedObject
    
        var persistentContainer : NSPersistentContainer {get}
        var idName : String {get}
        func loadFromDB(storableClass : ManagedObject.Type, id: ID) throws -> CoreDataResult<ManagedObject>
        func update(storableClass : ManagedObject.Type, id: ID, fields: [String : Any]) throws
        func fetch(request: NSFetchRequest<NSFetchRequestResult>, from: NSManagedObjectContext) -> CoreDataResult<ManagedObject>
        init(persistentContainer : NSPersistentContainer)
    }
    
    extension CoreDataWriteManagerProtocol {
        private func loadFromDB(storableClass : ManagedObject.Type, id: ID) -> CoreDataResult<ManagedObject>{
            let predicate = NSPredicate(format: "%@ == %@", idName, id)
    
            let fetchRequest = storableClass.fetchRequest()
            fetchRequest.predicate = predicate
    
            return fetch(request: fetchRequest, from: persistentContainer.viewContext)
        }
    
        func fetch(request: NSFetchRequest<NSFetchRequestResult>, from context: NSManagedObjectContext) -> CoreDataResult<ManagedObject> {
            guard let results = (try? context.fetch(request)) as? [ManagedObject] else {
                return .failure(CoreDataResponseError.fetch(entityName: request.entityName ?? "Empty Entity Name")) // @TODO not sure if entityName gets passed or not.
            }
            if let result = results.first {
                return .success(result)
            }else{
                return .failure(CoreDataResponseError.idDoesNotExist)
            }
        }
    }