如何将类型应用于 NSFetchRequest 实例?

Posted

技术标签:

【中文标题】如何将类型应用于 NSFetchRequest 实例?【英文标题】:How to apply the type to a NSFetchRequest instance? 【发布时间】:2016-06-14 11:39:59 【问题描述】:

在 Swift 2 中,以下代码可以正常工作:

let request = NSFetchRequest(entityName: String)

但在 Swift 3 中会报错:

无法推断通用参数“ResultType”

因为NSFetchRequest 现在是泛型类型。他们在他们的文件中写道:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

所以如果我的结果类是例如Level 我应该如何正确请求?

因为这不起作用:

let request: NSFetchRequest<Level> = Level.fetchRequest

【问题讨论】:

链接到新功能,我在这里找到了代码:developer.apple.com/library/prerelease/content/releasenotes/… 是一个方法,所以应该是let request: NSFetchRequest&lt;Level&gt; = Level.fetchRequest() 或者只是let request = Level.fetchRequest() @MartinR 这不会通过编译,因为它是模棱两可的。 @MartinR 似乎堆栈溢出成员非常喜欢你。他们会盲目地支持你。 :P 【参考方案1】:
let request: NSFetchRequest<NSFetchRequestResult> = Level.fetchRequest()

let request: NSFetchRequest<Level> = Level.fetchRequest()

取决于你想要的版本。

您必须指定泛型类型,否则方法调用不明确。

第一个版本是为NSManagedObject定义的,第二个版本是为每个使用扩展名的对象自动生成的,例如:

extension Level 
    @nonobjc class func fetchRequest() -> NSFetchRequest<Level> 
        return NSFetchRequest<Level>(entityName: "Level");
    

    @NSManaged var timeStamp: NSDate?

重点是删除字符串常量的使用。

【讨论】:

那么对于每个实体,我需要添加扩展代码吗?或者那是自动发生的?所以如果我有一个“狗”实体和“猫”实体,我需要“扩展狗@nonobjc...”和“扩展猫@nonobjc...”吗? @DaveG 该扩展是自动为您生成的。 好吧,你,但我有点困惑,因为我尝试了 'let fetchRequest = NSFetchRequest(entityName: "myEntityName")' 并且我收到错误“使用未声明的类型”myEntityName " 注意:方法 fetchRequest() 仅适用于 ios 10 @Sulthan 嗨,当我尝试使用您的代码时,出现以下错误。 Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'【参考方案2】:

我想我这样做是为了让它工作:

let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Level")

至少它从数据库中保存和加载数据。

但感觉这不是一个合适的解决方案,但它现在有效。

【讨论】:

我更喜欢这个解决方案,因为我以前有一个方法,它把实体名称作为参数,然后传回一个 NSManagedObjects 数组。 也喜欢这个,因为它不需要创建自定义类。只能使用实体名称!【参考方案3】:

我发现在 3.0 中工作的最简单的结构如下:

let request = NSFetchRequest<Country>(entityName: "Country")

其中数据实体类型是国家。

但是,当尝试创建 Core Data BatchDeleteRequest 时,我发现此定义不起作用,您似乎需要使用表单:

let request: NSFetchRequest<NSFetchRequestResult> = Country.fetchRequest()

即使 ManagedObject 和 FetchRequestResult 格式应该是等效的。

【讨论】:

这个答案中提到的第一个结构是我目前可以在 Swift3 / iOS 10 / Xcode 8 上使用我获取的结果控制器进行编译的唯一方法。 这是我尝试各种形式后的经验。他们是否涵盖了 CoreData 演示文稿中的任何其他形式?打算明天去看看…… 第一个例子是我发现的最简单的方法,无需使用if #available(iOS 10.0) ... 条件【参考方案4】:

这里有一些通用的 CoreData 方法可能会回答您的问题:

import Foundation
import Cocoa

func addRecord<T: NSManagedObject>(_ type : T.Type) -> T

    let entityName = T.description()
    let context = app.managedObjectContext
    let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
    let record = T(entity: entity!, insertInto: context)
    return record


func recordsInTable<T: NSManagedObject>(_ type : T.Type) -> Int

    let recs = allRecords(T.self)
    return recs.count



func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]

    let context = app.managedObjectContext
    let request = T.fetchRequest()
    do
    
        let results = try context.fetch(request)
        return results as! [T]
    
    catch
    
        print("Error with request: \(error)")
        return []
    


func query<T: NSManagedObject>(_ type : T.Type, search: NSPredicate?, sort: NSSortDescriptor? = nil, multiSort: [NSSortDescriptor]? = nil) -> [T]

    let context = app.managedObjectContext
    let request = T.fetchRequest()
    if let predicate = search
    
        request.predicate = predicate
    
    if let sortDescriptors = multiSort
    
        request.sortDescriptors = sortDescriptors
    
    else if let sortDescriptor = sort
    
        request.sortDescriptors = [sortDescriptor]
    

    do
    
        let results = try context.fetch(request)
        return results as! [T]
    
    catch
    
        print("Error with request: \(error)")
        return []
    



func deleteRecord(_ object: NSManagedObject)

    let context = app.managedObjectContext
    context.delete(object)


func deleteRecords<T: NSManagedObject>(_ type : T.Type, search: NSPredicate? = nil)

    let context = app.managedObjectContext

    let results = query(T.self, search: search)
    for record in results
    
        context.delete(record)
    


func saveDatabase()

    let context = app.managedObjectContext

    do
    
        try context.save()
    
    catch
    
        print("Error saving database: \(error)")
    

假设有这样一个联系人的 NSManagedObject 设置:

class Contact: NSManagedObject

    @NSManaged var contactNo: Int
    @NSManaged var contactName: String

这些方法可以通过以下方式使用:

let name = "John Appleseed"

let newContact = addRecord(Contact.self)
newContact.contactNo = 1
newContact.contactName = name

let contacts = query(Contact.self, search: NSPredicate(format: "contactName == %@", name))
for contact in contacts

    print ("Contact name = \(contact.contactName), no = \(contact.contactNo)")


deleteRecords(Contact.self, search: NSPredicate(format: "contactName == %@", name))

recs = recordsInTable(Contact.self)
print ("Contacts table has \(recs) records")

saveDatabase()

【讨论】:

干净优雅。希望我能投票赞成 100!一个修饰,想知道你的想法,我用 context?.perform() 包装了每个方法以确保线程安全。这是 Apple 推荐的。 不是很OO。除非您能够将这些编写为 NSManagedObjectContect 的扩展,否则这将是一个不错的解决方案。 刚刚注意到 - 计算您正在检索它们的所有记录,然后计算数组条目的数量 - 这确实效率低下。您可能希望扩展 recordsInTable 函数以利用 context.count(request) 这些是很好的补充,应该有更多的投票,但可能不是因为它偏离了主要问题(尽管它很有用)。我建议很难使用删除功能进行更改,而是使用 NSManagedObjectID 删除。所以在context.delete(record) 之前添加let record = context.object(with: record.objectID) 并使用该记录对象删除。【参考方案5】:

这是迁移到 Swift 3.0 最简单的方法,只需添加 &lt;Country&gt;

(经过测试和工作)

let request = NSFetchRequest<Country>(entityName: "Country")

【讨论】:

【参考方案6】:

Swift 3.0 这应该可以工作。

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate

【讨论】:

【参考方案7】:

我也有“ResultType”无法推断错误。一旦我重建将每个实体的 Codegen 设置为“类定义”的数据模型,它们就会清除。我在这里做了一个简短的文章,并附有分步说明:

Looking for a clear tutorial on the revised NSPersistentContainer in Xcode 8 with Swift 3

“重建”是指我创建了一个具有新条目和属性的新模型文件。有点乏味,但确实有效!

【讨论】:

【参考方案8】:

到目前为止,最适合我的是:

let request = Level.fetchRequest() as! NSFetchRequest<Level>

【讨论】:

【参考方案9】:

我遇到了同样的问题,我通过以下步骤解决了它:

选择您的 xcdatamodeld 文件并转到数据模型检查器 选择您的第一个实体并转到部分类 确保选中 Codegen“类定义”。 删除所有生成的实体文件。你不再需要它们了。

在这样做之后,我不得不删除/重写所有出现的 fetchRequest,因为 XCode 似乎以某种方式与代码生成的版本混淆了。

HTH

【讨论】:

【参考方案10】:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

func loadItemsCategory() 

    let request: NSFetchRequest<Category> = Category.fetchRequest()
    
    do 
        categoryArray = try context.fetch(request)
     catch 
        print(error)
    
    
    tableView.reloadData()
    

【讨论】:

以上是关于如何将类型应用于 NSFetchRequest 实例?的主要内容,如果未能解决你的问题,请参考以下文章

如何将类型应用于NSFetchRequest实例?

技术实操如何使用GoLang将JT808协议中的DWORD类型转为string?

NSFetchRequest 返回具有 <invalid> 类型元素的数组

调用返回多个集合时的 ​​NSFetchRequest

将 NSPredicate 设置为 NSFetchRequest 后 executeFetchRequest 崩溃

如何将此 SQL 查询转换为 NSFetchRequest