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

Swift通用表视图数据源和委托,协议支持的NSManagedObjects,无法获取实体属性,运行时错误

  •  2
  • andrewbuilder  · 技术社区  · 6 年前

    问题

    break point screenshot

    ***由于未捕获的异常“NSUnknownKeyException”而终止应用程序,原因:“[valueForUndefinedKey:]: 此类与活动密钥的键值编码不兼容。'

    answer SO question ,我在我的代码中插入了一个异常断点,现在它会在这一行暂停执行。。。

    static var entityActive: Bool {
        return entity.value(forKey: "active") as! Bool // <-- PAUSES AT THIS LINE
    }
    

    这显然需要进一步解释。。。

    背景

    我正在编写一个核心数据应用程序,它使用一个通用的表视图数据源和委托,或多或少是根据Florian Kugler的书《核心数据》建立的,该书于2017年由 objc.io

    我成功地将三个独立的 UITableViewController s到该通用数据源/委托。我使用一个主情节提要,这三个控制器链接到三个主情节提要 UISplitViewController

    我已经删除了脚本中自动生成的数据源和代理连接 UITableView s(尽管我是离开还是删除这些连接,似乎没有什么区别)。

    如果我注释掉 static var entityActive 如上所述,该项目将成功构建并运行。

    UITableViewDelegate 方法 tableView(_, willDisplay:, forRowAt:) 改变现状 .textColor 文本和 .backgroundColor 基于属性“active”的单元格数目,存储为每个实体的布尔标量类型值。

    为清楚起见,我试图为每个实体获取“活动”属性(我的数据模型中的每个实体都有一个公共属性“活动”) NSManagedObject 对于实体。“活动”属性的值( Bool true false )然后,在下面的代码中,使用for每个实体格式化if…else语句中的单元格。

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    
        let entityObjectActive = T.entityActive
    
        if entityObjectActive == true {
            cell.textLabel?.textColor = UIColor.black
            cell.detailTextLabel?.textColor = UIColor.darkGray
            cell.backgroundColor = UIColor.white
        } else if entityObjectActive == false {
            cell.textLabel?.textColor = UIColor.lightGray
            cell.detailTextLabel?.textColor = UIColor.lightGray
            cell.backgroundColor = UIColor.clear
        }
    
    }
    

    编译器没有抱怨。

    T (表示核心数据实体)以关联静态属性 entityActive 以我为例 entityObjectActive -所以这似乎有效。。。

    let entityObjectActive = T.entityActive
    

    为确认,我有以下信息:

    class MyDataSource<T: Managed, 
                       Delegate: TableViewDataSourceDelegate>: 
                       NSObject, 
                       UITableViewDataSource, 
                       UITableViewDelegate, 
                       NSFetchedResultsControllerDelegate {
    
        // lots of code...
    }
    

    protocol Managed: class, NSFetchRequestResult {
        static var entity: NSEntityDescription { get }
        static var entityActive: Bool { get } }
    }
    

    和扩展。。。

    extension Managed where Self: NSManagedObject {
        static var entity: NSEntityDescription { return entity()  }
        static var entityActive: Bool {
            return entity.value(forKey: "active") as! Bool
        }
    }
    

    解决问题的尝试

    How to fix Error: this class is not key value coding-compliant for the key tableView.'

    Uncaught exception: This class is not key value coding-compliant

    setValue:forUndefinedKey: this class is not key value coding-compliant for the key

    似乎所有的问题都是如此;A与故事板中IB连接的问题直接相关。也许这也是我的问题——但如果是这样的话,在我看来,它似乎隐藏得很好。

    有什么帮助或建议吗?

    PS:我在Obj-C做了很长时间的编码员之后,正在学习Swift,我真的在为泛型协议扩展范式的转变而挣扎,所以我对泛型类型的使用可能是不正确的?

    1 回复  |  直到 6 年前
        1
  •  0
  •   andrewbuilder    6 年前

    感谢那些为我指出这个解决方案的人。。。

    我的修订协议 Managed

    protocol Managed: class, NSFetchRequestResult {
        static var entity: NSEntityDescription { get }
        var attributeActive: Bool { get }  // <-- REMOVED static 
    }
    

    我的修订延期 管理 ...

    var attributeActive 分机。。。

    extension Managed where Self: NSManagedObject {
        static var entity: NSEntityDescription { return entity()  }
    
        var attributeActive: Bool {
             guard let attribute = self.value(forKey: "active") as? Bool else {
                 return false // in case key "active" is not set
             }
             return attribute
        }
    }
    

    更新-已删除 var属性

    我的三个核心数据实体(其数据显示在三个单独的 UITableViewController s) 。。。

    extension <<DataModelEntity>>: Managed {    
        public var attributeActive: Bool {
            return self.active
        }
    
        @NSManaged public var active: Bool
        @NSManaged public var <<OTHER DATA MODEL ENTITY ATTRIBUTES>> //...
        // ...etc.
    }
    

    也许值得注意的是,为了清楚起见 编码基因 数据模型中每个实体的值设置为 ,所以我手动 1.

    1. 当我手工书写时,我的意思是我使用了 创建托管对象子类。。。 管理

    最后,我修改的通用数据源委托类。。。

    class MyDataSource<T: Managed, 
                       Delegate: TableViewDataSourceDelegate>: 
                       NSObject, 
                       UITableViewDataSource, 
                       UITableViewDelegate, 
                       NSFetchedResultsControllerDelegate {
    
        // lots of code...
    
        func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    
            let object = fetchedResultsController.object(at: indexPath)
            let objectActive = object.attributeActive
    
            if objectActive == true {
                cell.textLabel?.textColor = UIColor.black
                cell.detailTextLabel?.textColor = UIColor.darkGray
                cell.backgroundColor = UIColor.white
            } else if entityObjectActive == false {
                cell.textLabel?.textColor = UIColor.lightGray
                cell.detailTextLabel?.textColor = UIColor.lightGray
                cell.backgroundColor = UIColor.clear
            }
        }
    
        // lots more code...
    
    }
    

    坦白地说,我仍然在自己弄清楚细节,并计划在将来随着我对swift泛型、协议和扩展的理解的提高,添加一个更简洁/准确的理由,但现在我提供以下内容。。。

    正如评论中指出的,我错误地试图从实体描述中获取基于数据模型属性的实体属性。这就像是通过询问乐高积木盒中某块积木的特征来获得乐高积木的颜色或大小。”如果乐高盒子可以问这样的问题,盒子可能会问。

    基本上我犯了几个错误。我不理解静态定义对我的变量的影响,也不理解我的 协议和扩展与采用该协议的任何类交互。