在本教程中,您将学习如何通过具有批处理插入,持久历史记录和派生属性的高效核心开奖结果3d使用来了解如何改进您的iOS应用程序。


这是原始条目的同伴讨论主题 //www.ohdvia.icu/14958063-modern-efficient-core-data

你好!非常感谢这个教程!

我一直在用Swift 5,iOS 14,Xcode 12批量插入/预加载开奖结果3d批量插入/预加载开奖结果3d。我想做的一件事是更改此代码,使其从捆绑的JSON文件中批量插入,而不是JSON文件在网上。任何指针?你怎么样做这件事?您可以提供的任何信息都非常感谢。

我走了一点进展。

首先我在nsmanagedObject子类中创建一个函数:

导入coredata.
进口Swifui.

扩展人员{

@NSManaged var name: String?

static func createSingle(name: String?, using viewContext: NSManagedObjectContext) {
    let person = Person(context: viewContext)
    person.name = name
    
    do {
        try viewContext.save()
    } catch {
        let nserror = error as NSError
        fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        
    }
    
}

}

然后我将此放在ContentView中,以按一个按钮才能将一些JSON加载到核心开奖结果3d中,并给我一个数量已加载多少名称。

进口Swifui.
导入coredata.

struct contentView:查看{
@环境(.managedObjectContext)Private Var ViewContext

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Person.name, ascending: true)],
    animation: .default) private var people: FetchedResults<Person>

@State var name: String = ""


var body: some View {
    let str = "{\"names\": [\"Dave\", \"Tim\", \"Tina\"]}"
    let data = Data(str.utf8)
    //        let str1 = "{\"names\": [\"Bob\"]}"
    //        let data = Data(str1.utf8)
    
    VStack{
        Text("\(people.count)")
        Button(action: {
            do {
                // make sure this JSON is in the format we expect
                if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                    // try to read out a string array
                    if let names = json["names"] as? [String] {
                        for name in names {
                            self.name = name
                            Person.createSingle(name: self.name, using: self.viewContext)
                            print(name)
                        }
                        //                        name = names[0]
                        //                        Person.createSingle(name: self.name, using: self.viewContext)
                        //                        print(people)
                    }
                }
            } catch let error as NSError {
                print("Failed to load: \(error.localizedDescription)")
            }
            
        }) {
            Text("Add Names")
        }
    }
    
}

}

你好 @dmalicke., Happy new year. If you like you can include a .json file in your app’s bundle and create a class much like the RemoteDataSource class from the article. The main difference will be you get the URL to the file using Bundle.main.url(forResource:withExtension:). Otherwise it’ll be very similar.

@atetlaw. 非常感谢您的本教程。我正在使用下载大JSON的应用程序的信息。但是我有问题:我如何管理关系(一对一,一多倍)?

谢谢你

你好 @rufy.遗憾的是,批处理插入请求的局限性之一是他们无法设置关系,但它们会不会留下现有的关系。这意味着您需要手动执行关系设置。但是,一旦设置了关系,就可以在不打破关系的情况下安全地更新实体开奖结果3d。

控制关系可能有多种方式。如果您有办法定义所下载的开奖结果3d中的哪些子实体属于哪些子实体,也许使用父ID后,完成批处理插入后,您可能能够将该父ID与组组合在一起在fetch请求中,手动设置父级。

当然,它会取决于有多少实体以及需要花多少时间。

希望有帮助,
安德鲁

@atetlaw. 您的想法很有趣,但遗憾的是,我发现2个表的开奖结果3d有些问题。您的教程问题不幸的是,您只在一个表中输入开奖结果3d。如果您的教程将开奖结果3d插入到例如3个表(父亲和2个孩子)组成的开奖结果3d库中,我认为这将是更完整和有用的。您不仅会看到请求的限制,还可以看到它如何影响时间。
请您有办法发布示例代码,显示如何在论坛中处理这些案例,以便利益Raywnderlich读者?
我会很感激。

我的应用程序有点像火球应用程序。我要添加开奖结果3d,开奖结果3d输入的时间非常重要

@atetlaw.
我正在尝试应用您的建议,我有一个问题:是否可以在您的方法中插入多个批处理插入请求?因为每个批处理插入请求都有一个特定的实体。

private func newBatchInsertRequest(with fireballs: [FireballData]) -> NSBatchInsertRequest {
    // 1
    var index = 0
    let total = fireballs.count
    
    // 2
    let batchInsert = NSBatchInsertRequest( entity: Fireball.entity()) { (managedObject: NSManagedObject) -> Bool in 
   // 3
   guard index < total else { return true }

if let fireball = managedObject as? Fireball {
  // 4
  let data = fireballs[index]
  fireball.dateTimeStamp = data.dateTimeStamp
  fireball.radiatedEnergy = data.radiatedEnergy
  fireball.impactEnergy = data.impactEnergy
  fireball.latitude = data.latitude
  fireball.longitude = data.longitude
  fireball.altitude = data.altitude
  fireball.velocity = data.velocity
  }

  // 5
   index += 1
   return false
  }
 return batchInsert
}

或者我必须创建尽可能多的方法,因为我下载了表?在这种情况下,我也必须乘以context.execute(_请求:)命令?

private func batchInsertFireballs(_ fireballs: [FireballData]) {
  // 1
  guard !fireballs.isEmpty else { return }

  // 2
  container.performBackgroundTask { context in
    // 3
    let batchInsert = self.newBatchInsertRequest(with: fireballs)
    do {
      try context.execute(batchInsert)
    } catch {
      // log any errors
    }
  }
}

你好 @rufy. 如果我清楚地了解你的问题,这是一个快速的回答:

  • 是的,每个批量插入请求只能插入1个实体类型
  • in the article I made a batchInsertFireballs method only because I only had 1 entity type as you pointed out. But you can make your own method to replace it and within the call container.performBackgroundTask {...} you can create and execute as many batch inserts as you like. But of course they still won’t update the relationships unless you specifically do that.

如果你能发布一些代码,你就可以了解你的情况一点更好,并且可以给出更好的建议。

你好 @atetlaw. 非常感谢您的回答。

我试图简化我的两个例子的情况,所以读这个帖子的每个人都得到了帮助。

前提:
我从我的服务器下载开奖结果3d并解码结果JSON。解码后,我包含一个包含:
JSON:[
自由基:[radicaldata]
汉字:[kanjidata] *
]

* kanjidata:[
汉子:…
激进的:…

kunyomi:[kunyomidata]
onyomi:[onyomidata]
]

情况1:
我有两个实体:

  • 激进
  • 汉子
    与关系一对多(一个激进有很多汉字;一只汉字只有一个激进的)。

因此,要正确插入每个开奖结果3d,我必须插入第一个激进和kanji后,但是当我插入kanji时,我想插入它的激进术。

func newBatchInsertRequest(with jsonDatabase: [JaappJSON], context:NSManagedObjectContext) -> NSBatchInsertRequest? {
        let database = jsonDatabase[0]
        if let radicali = database.radicali {
            var index = 0
            let total = radicali.count
            
            let batchInsert = NSBatchInsertRequest(entity: Radicale.entity()) { (managedObject: NSManagedObject) -> Bool in
                guard index < total else { return true }
                
                if let radicale = managedObject as? Radicale {
                    // 4
                    let data = radicali[index]
                    radicale.concettoEN = data.concettoEN
                    radicale.concettoITA = data.concettoITA
                    radicale.nomeKana = data.nomeKana
                    radicale.numTratti = data.numTratti!
                    radicale.radicale = data.radicale
                    radicale.varianti = data.varianti
                }
                index += 1
                return false
            }
            return batchInsert
        }
        if let kanjiList = database.kanji {
            var index = 0
            let total = kanjiList.count
            
            let batchInsert = NSBatchInsertRequest(entity: Kanji.entity()) { (managedObject: NSManagedObject) -> Bool in
                guard index < total else { return true }
                if let kanji = managedObject as? Kanji {
                    let data = kanjiList[index]
                    kanji.concettoEN = data.concettoEn
                    kanji.concettoITA = data.concettoIta
                    kanji.confrontaCon = data.confrontaCon
                    kanji.contrario = data.contrario
                    kanji.frequenza = data.frequenza!
                    kanji.idDatabase = data.idDatabase!
                    kanji.idHalpern = data.idHelpern!
                    kanji.idNewNelson = data.idNewnelson!
                    kanji.jlptLevel = data.jlptLevel!
                    kanji.jouyouGrade = data.jouyouGrade!
                    kanji.kanji = data.kanji
                    kanji.numTratti = data.numTratti!
                    kanji.radicale = data.radicale
                    kanji.spiegConcettoEN = data.spiegConcettoEn
                    kanji.spiegConcettoITA = data.spiegConcettoIta
                    kanji.unicode = data.unicode
                    
                    do {
                        let requestRadicale:NSFetchRequest<Radicale> = Radicale.fetchRequest()
                        let listaRadicali = try context.fetch(requestRadicale)
                        kanji.radicaleRelazione = listaRadicali[0]
                    } catch {
                        kanji.radicaleRelazione = nil
                    }
                   //Here situation 2
                }
                index += 1
                return false
            }
            return batchInsert
        }
        return nil
    }

情况2:
我有三个实体:

  • 汉子
  • kunyomi
  • onyomi.
    与关系一对多(一个汉字有许多Kunyomi;一只汉字有很多Onyomi)。

正如您可以在前提下看到的,Kunyomi和Onyomi属于Kanji位于L'Object Kanjidata内。所以在这种情况下,我想在插入kanji时设置这种关系。

情况2代码列表:

if let kunYomiSet = kanji.lettureKun {
                    let kunYomiList = kunYomiSet.allObjects as! [KunYomiData]
                    var indexKunYomi = 0
                    let totalKunYomi = kunYomiList.count
                    //TODO: inserisci kun yomi
                    _ = NSBatchInsertRequest(entity: KunYomi.entity()) { (managedObject:NSManagedObject) -> Bool in
                        guard indexKunYomi < totalKunYomi else { return true }
                        if let kunYomi = managedObject as? KunYomi {
                            let data = kunYomiList[index]
                            kunYomi.kunYomiKana = data.kunYomiKana
                            kunYomi.kunYomiKanji = data.kunYomiKanji
                            kunYomi.kunYomiRomaji = data.kunYomiRomaji
                            kunYomi.kunYomiVideo = data.kunYomiVideo
                            kanji.addToLettureKun(kunYomi)
                        }
                        indexKunYomi += 1
                        return false
                    }
                }
                if let onYomiSet = kanji.lettureOn {
                    let onYomiList = onYomiSet.allObjects as! [OnYomiData]
                    var indexOnYomi = 0
                    let totalOnYomi = onYomiList.count
                    _ = NSBatchInsertRequest(entity: OnYomi.entity(), managedObjectHandler: { (managedObject:NSManagedObject) -> Bool in
                        guard indexOnYomi < totalOnYomi else { return true }
                        if let onYomi = managedObject as? OnYomi {
                            let data = onYomiList[index]
                            onYomi.onYomi = data.onYomi
                            onYomi.onYomiRomaji = data.onYomiRomaji
                            kanji.addToLettureOn(onYomi)
                        }
                        indexOnYomi += 1
                        return false
                    })
                }

我该怎么做?

我希望我说过我必须面对的两种情况,以便正确插入。

你好,我们又见面了, @rufy. !!这是肯定的。

我有点难以给你良好的建议,因为我不知道你正在插入的开奖结果3d量,我看不出人们如何工作 - 你如何知道哪个激进属于哪个汉字?

我的第一位建议是首先在设置关系之前首先执行所有插入,我怀疑尝试获取要使用批处理插入的开奖结果3d时出现问题,同时执行插入。您需要完成批处理插入的执行,然后执行新任务并在获取任何开奖结果3d之前获取新的托管对象上下文。

这对我来说听起来很棘手,现在我想知道批量插入请求是否真的适合您的情况?

你好 @atetlaw.,实际上我需要插入更多30000的记录。

我看不出人际关系如何工作 - 你如何知道哪个激进的属于汉字?

激进实体具有称为“激进”的属性,其中包含字符。
汉子实体具有称为“激进”的属性,其中包含该字符。

但是,您可以使用属性ID和ID_radical的Tipica方法,如果您更好。

这对我来说听起来很棘手,现在我想知道批量插入请求是否真的适合您的情况?

这是一个很好的问题。但是很奇怪,没有办法迅速建立这种关系。
让我解释一下:我的问题不是如何设置这种关系。因为如果阅读我在这篇文章中发布的代码 nsbatchinsertrequest和关系
我做你建议的事情:首先插入和关系。
我的问题是时间。当我已经说过没有设置关系的插入持续5秒。关系的设置35秒。要执行此操作,我循环所有激进术并查找对象数组中的kanji对象并设置关系。我知道过滤器函数具有时间复杂度的O(n)。这是问题吗?是否有替代过滤功能?也许我已经使用了fetchrequest?

我明白考虑了一个你没有的开奖结果3d库可以复杂。所以尝试考虑由彼此相关的不同实体组成的开奖结果3d库(它不必大。3,4个实体也很好)。并想象开奖结果3d库以30-40000记录填充。您如何输入所有这些记录以及如何在最短的时间内建立关系?请记住,在使用应用程序之前,用户必须等待下载和开奖结果3d库人口来完成(并且这就是难度谎言。时间。)
你会怎么做?

非常感谢你的耐心

我有更多的问题和一些想法。

您是否需要在每次发布或仅启动时加载30,000 kanji?

如果只在第一次启动和初始的kanji开奖结果3d集中始终相同,则可以考虑将其预加载到您的App Bundle中。在第一次启动时启动所有初始开奖结果3d时已经存在。

You mention that each radical has multiple kanji, but each kanji only has 1 radical? If so it may be more efficient to set the relationship from the radical side: loop through the radicals and use the kanji IDs to fetch all relevant Kanji, then set the radical’s kanjis property (or whatever that’s called in your model) to the set of Kanji you fetched. Using a predicate like id IN [ID array] is an efficient fetch in my experience. This might mean less work if it results in less fetches.

如果批量插入速度快,则还可以考虑避免设置CoreData关系。这意味着您将有4个表,并且您可以使用只使用项目ID的获取请求在应用程序中显示相关项目。例如,如果您有kanji显示屏幕,则只需使用id_radical值获取适当的激进术。所以你可以显示它。你松散了Coreedata的对象关系管理,但进口了更快!这将取决于您要使用的用户体验。

想要继续榜样(原因是因为信息不仅是kanji),我的想法是在第一个启动App的第一个开始时下载开奖结果3d库,那么该应用程序将下载我发布的更新。所以从这一点来看,你已经可以明白30,000现在,但后来就可以了。

我理解你的想法,但正如我早些时候所说,起动器套件不会静止。我将在开奖结果3d库中插入新开奖结果3d。如果我用捆绑开奖结果3d集发布应用程序,这意味着我必须随着时间的推移保持它。我更愿意将开奖结果3d库和应用程序功能分开。

由于您提到的原因,我已经实施了这个想法。我所拥有的唯一问题是,使用当前代码I循环通过激进的阵列,并且每个激进的滤波器都能找到Kanji。如果我不得不循环循环阵列并获取谓词以查找所有数组,则应用程序在第二轮崩溃。我仍然不明白为什么。请记住,在设定关系时,我尚未保存上下文。这可能是这个问题吗?

然而,在引理的第二种情况下,其中许多着作和许多含义,事情是不同的。我们正在谈论大约7000个字字和7000脚本和7000个含义。这是这种情况,需要超过20-30秒。

我也非常了解这个解决方案。一方面,我会被诱惑。但这是一个保留两个实体的另一个代码。核心开奖结果3d使您可以更容易地使用实体和关系,就像它们是类一样,丢失此功能......我不知道它有多便宜。目前在线在线的应用程序的版本使用私有上下文但不使用BatchInsertRequest,并且目前下载和人口持续时间为35秒。我想使用bachinsertrequest来加快整个过程。如果我没有考虑到这种关系,我可以(约30000条记录在大约5秒内保存。非常快!)但是可以从计算的角度来设置一个关系如此昂贵吗?是循环吗?我应该使用映射吗?

我必须仔细考虑一下。如果您有其他建议,则欢迎他们。无论如何,我要感谢您给我的各种建议。我希望我们能够找到一个解决方案。显然,如果我找到一些东西,我会立即通知你。

你好,我们又见面了 @rufy. !

批量插入超快速,因为它们在开奖结果3d库级别运行,并且没有将开奖结果3d加载到内存中。 CoreData获取快速,但它确实涉及将开奖结果3d移动到内存中,并且当您有很多开奖结果3d来处理时,这就是将其慢下来的。不幸的是,一次修改30,000条记录将需要那么长时间。我以为35秒对此有多少工作非常适合! :笑脸:

几件事可以取得更慢(但是你需要测试他们是否需要测试):

  • avoid reading data from the fetched record if all you want to do is set the relationship. OR if you know you need to read a property on the record set returnsObjectsAsFaults to false on the fetch requests.
  • avoid using performAndWait on the NSManagedObjectContext, prefer perform instead.

我建议的另一件事是批量导入,然后用批处理更新关系。将每个批处理作为单独的操作执行。至少,您将在UI中出现一些内容,并且您的用户可以开始使用该应用程序。你将不得不对渐进式加载感到满意。