通过对象的枚举属性使用 NSPredicate 获取核心数据托管对象的问题

Posted

技术标签:

【中文标题】通过对象的枚举属性使用 NSPredicate 获取核心数据托管对象的问题【英文标题】:Issue with fetching Core Data managed object using NSPredicate by object's enum attribute 【发布时间】:2020-02-09 23:40:36 【问题描述】:

我在获取具有enum 属性和Int16 rawValues 的Core Data 托管对象时遇到问题。调用NSPredicate(format:)时程序崩溃。

这是导致崩溃的代码(第 3 行):

func returnBook (bookType: BookType) -> Book? 
    let fetchRequest = NSFetchRequest<Book>(entityName: "Book")
    fetchRequest.predicate = NSPredicate(format: "%K = %@", #keyPath(Book.bookType), bookType.rawValue as CVarArg)
    fetchRequest.fetchLimit = 1
    let book = (try? fetchRequest.execute())?.first
    return book

崩溃消息是:

线程 1:EXC_BAD_ACCESS(代码=1,地址=0x3)

当我用以下内容更改第 3 行时,我得到了另一种错误。

这是替代代码:

fetchRequest.predicate = NSPredicate(format: "%K = %@", #keyPath(Book.bookType), bookType as! CVarArg)

这是替代代码导致的错误:

线程 1:信号 SIGABRT

我在调试输出中看到了这一点:

无法转换类型“ProjectName.BookType”的值 (0x10ac10098) 到 'Swift.CVarArg' (0x10b698548)。

以下是项目中的一些其他相关代码:

@objc(Book)
public class Book: NSManagedObject 
    // …


extension Book 

    @NSManaged public var bookType: BookType



@objc public enum BookType: Int16 
    case fiction    = 0
    case nonFiction = 1
    case reference  = 2

我的问题:如何使用 bookType 属性从我的 Core Data 存储中正确获取 Book 对象,该属性位于 @objc enum 类型和 Int16 rawValues 中?

更新:

我已经按照 Jesse Squires 的建议 in a presentation 尝试了其他方法:

    enum 的定义中删除了@objc 关键字 使用privatepublic 属性访问enum 的实例...

我已按照链接演示文稿中的建议更改了我的代码,并将 Core Data 的“.xcdatamodeld”文件中的bookType 属性名称更改为bookTypeValue

extension Book 

    public var bookType: BookType 
        get 
            return BookType(rawValue: self.bookTypeValue)!
        
        set 
            self.bookTypeValue = newValue.rawValue
        
    

    @NSManaged private var bookTypeValue: Int16



public enum BookType: Int16 
    case fiction    = 0
    case nonFiction = 1
    case reference  = 2

我尝试使用上面链接的演示文稿中建议的私有值来获取...:

fetchRequest.predicate = NSPredicate(format: "bookTypeValue == %@", bookType)

但是我也无法用这种方法解决我的问题...... XCode 立即抱怨:

参数类型“BookType”不符合预期的类型“CVarArg”

如果我改用bookType.rawValue,XCode 就会停止抱怨。但是,程序在运行时崩溃并出现上述相同的错误(第一个)。

更新 2:

我已将社区建议的答案应用到this question。更具体地说,我已经尝试了使用%i@ld 而不是%@ 的问题答案中的建议,如下所示:

fetchRequest.predicate = NSPredicate(format: "bookTypeValue == %i", bookType.rawValue)

我尝试了另一个使用字符串插值的建议,如下所示:

fetchRequest.predicate = NSPredicate(format: "bookTypeValue == %@", "\(bookType.rawValue)")

这些代码更改防止了崩溃,但 func returnBook 返回 nil,即使我确定数据在那里。由于某种原因,获取或比较失败...

【问题讨论】:

我猜bookType必须在模型编辑器中定义为Transformable?当你把它重命名为 bookTypeValue 时,你是不是把它改成了 Integer?谓词不适用于可转换属性(至少在获取时不行)。 @pbasdf bookType 在模型编辑器中被定义为 Int16(这里建议:***.com/a/31681870/3780985),在我将它重命名为 bookTypeValue 后,它在模型编辑器中仍然与 Int16 相同。我只是将代码中的类型从 BookType 更改为 Int16。 【参考方案1】:

在为这个问题苦苦挣扎了几个小时后,我终于设法解决了。

这是我按时间顺序为解决问题所做的工作:

    我已将bookTypeValue 的类型从Int16 更改为Int32,因为我在官方(存档)documentation 中看不到16 位整数的格式说明符。我已经测试过,我可以确认,如果您在此处使用 %@ 而不是正确的格式说明符,即 %d 用于有符号的 32 位整数,则会出现问题中的第一个错误:

线程 1:EXC_BAD_ACCESS(代码=1,地址=0x3)

    我已经像这样更改了NSPredicate(format:) 调用:

    fetchRequest.predicate = NSPredicate(格式: "bookTypeValue == %d", bookType.rawValue)

    我已经更改了returnBook func 中的第 5 行,如下所示:

     do 
        let results = try coreDataStack.managedContext.fetch(fetchRequest)
        if results.count > 0 
            book = results.first
            return book
         
      catch let error as NSError 
        print("Fetch error: \(error) description: \(error.userInfo)")
     
    

【讨论】:

以上是关于通过对象的枚举属性使用 NSPredicate 获取核心数据托管对象的问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 NSPredicate 过滤核心数据中的对象

使用 NSPredicate 过滤对象中的数组

如何将 @FetchRequest 属性包装器的新 nsPredicate 动态属性与传递给 View 的对象一起使用

带有核心数据对象的 NSPredicate

NSPredicate 获取具有 NSDate 日期范围内的日期属性的核心数据对象

NSPredicate 根据嵌套结构中的属性过滤自定义对象