保存和获取在核心数据中消耗高内存 800MB 和 100% CPU / 如何在 swift4 中在核心数据中进行批量插入和获取

0 xcode memory-leaks core-data ios swift

我使用核心数据存储了 10000-20000 条记录。如果我尝试保存和获取 10000 条记录,内存和 CPU 消耗很大,因为该应用程序在 iphone 6 plus 和更早的设备中崩溃。

对于 10000 条记录,图片不断增加到 800mb

以下是保存方法:----

   //InboxCoredata Saving Method i..(calling saving method ii)
func InboxSaveInCoreDataWith(array: [[String: AnyObject]])
{
    _ = array.map{self.InboxCreateCollectionEntityFrom(dictionary: $0)}
    do
    {
        try CoreDataStack.sharedInstance.persistentContainer.viewContext.save()
        print("Inbox Data saved Sucessfully in Coredata ")
    }
    catch let error
    {
        print(error)
    }
}

// Inbox Coredata saving method ii
func InboxCreateCollectionEntityFrom(dictionary: [String: AnyObject]) -> NSManagedObject?
{
    let context = CoreDataStack.sharedInstance.persistentContainer.viewContext
    if let inboxEntity = NSEntityDescription.insertNewObject(forEntityName: "InboxData", into: context) as? InboxData {
        inboxEntity.fileType = dictionary["FileType"] as? String
        inboxEntity.sender = dictionary["Sender"] as? String
        inboxEntity.mailPath = dictionary["MailPath"] as? String
        inboxEntity.fullMail = dictionary["FullMail"] as? NSObject
        inboxEntity.attachmentName = dictionary["AttachmentName"] as? String
        inboxEntity.size = dictionary["Size"]  as! Int32
        inboxEntity.date = dictionary["Date"] as? NSDate
        inboxEntity.dateForSearch = dictionary["DateForSearch"] as? String
        inboxEntity.inboxMail = dictionary["InboxMail"] as? String
        return inboxEntity
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

这是获取方法:----

 strongSelf.inboxDataFromCoreData(fetchLimit: 0) // Methods calling in viewdidload

//MARK: - Fetching inboxData from Coredata
func inboxDataFromCoreData(fetchLimit :Int)
{
    var inboxCoredataFetch = [[String : AnyObject]]()
    let context = CoreDataStack.sharedInstance.persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:  String(describing: InboxData.self))

    do {
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
        fetchRequest.fetchLimit = fetchLimit
        let results = try context.fetch(fetchRequest) as! [InboxData]
        print("inbox_coredata:\(results .count)")
        for data in results
        {
            let sender = data.sender
            let mailPath = data.mailPath
            let fileType = data.fileType
            let fullMail = data.fullMail
            let attachmentName = data.attachmentName
            let size = data.size
            let date = data.date
            let dateForsearch = data.dateForSearch
            let inboxMail = data.inboxMail

            inboxCoredataFetch.append(["Sender" : sender as AnyObject,"MailPath": mailPath! as AnyObject, "FileType":fileType as AnyObject, "FullMail":fullMail as AnyObject, "AttachmentName": attachmentName as AnyObject, "Size":size as AnyObject,"Date": date as AnyObject,"DateForSearch" :dateForsearch as AnyObject,"InboxMail":inboxMail as AnyObject])
        }



    }
    catch let err as NSError {
        print(err.debugDescription)
    }

    var sortingdata = inboxCoredataFetch as Array
    let mailBoxSortDescriptor = NSSortDescriptor(key: "Date", ascending:false, selector: nil)
    let dateDescriptor = NSSortDescriptor(key: "AttachmentName", ascending: true, selector: #selector(NSString.caseInsensitiveCompare))
    sortingdata = ((sortingdata as NSArray).sortedArray(using: [ mailBoxSortDescriptor,dateDescriptor]) )  as! [[String : AnyObject]]
    inboxTotalMailData = sortingdata

    if appDataLoadingFirst == true
    {
    appDataLoadingFirst = false
    displayTotalData = sortingdata
    DispatchQueue.main.async {
        self.hideActivityIndicator()
        self.collectionView.reloadData()
    }
    }

}
Run Code Online (Sandbox Code Playgroud)

核心数据结构是这样的:::---

在此处输入图片说明

我对这些有太多困惑的问题。

  1. 核心数据不好存储20000条记录??
  2. 每次我必须像这样刷新对象时,我是否需要 refreshObject(object,mergeChnage:bool) 以获取大量数据。
  3. Instruments 指示在 let results = try context.fetch(fetchrequest.....line 为什么?
  4. 我是否需要批量保存和获取数据,这会提高应用程序性能并减少内存吗??
  5. 为什么CPU有时会显示100%??
  6. 如果我在集合视图中显示 10000 条记录(从数组加载数据)会导致任何问题??如果是什么类型的问题??
  7. 需要您的宝贵建议并帮助我变得完美????

Tom*_*ton 6

您使用了大量内存和 CPU 时间,因为:

  • 创建新InboxData条目时,在保存更改之前,您需要为数组中的每个条目创建一个。您的代码意味着所有这些都必须同时在内存中。如果您有数千个条目,那将占用大量内存。
  • 当您获取条目时,您每次都会获取每个条目。同样,您的代码意味着您必须同时将所有这些都保存在内存中。同样,大量条目意味着大量内存。使用 Core Data 的主要原因之一是几乎总是可以只将数据的一个子集加载到内存中,而不是将所有数据加载到内存中。你正在加载一切。
  • 您将一堆属性从托管对象复制到字典中——因此现在您在内存中拥有两个数据副本,而不是一个。
  • 获取数据的代码对内存中的数据进行排序以创建sortingdata. 您已经将大量对象加载到内存中;现在你正在做大量的工作来对它们进行排序。这至少是您将 CPU 固定在 100% 的部分原因。
  • 整理数据后,你的结果分配给inboxTotalMailData displayTotalData,这是这个函数之外的某处。这意味着sortingdata即使在此功能完成后,其中的所有数据仍保留在内存中。

一些有帮助的事情:

  • 保存数据时,请定期保存——每 50 个条目,或每 100 个,或任何能产生良好结果的数字。不要尝试创建成千上万个对象并将它们全部保存在内存中。
  • 让获取请求对数据进行排序,而不是获取然后排序。获取请求可以有排序描述符,排序由 SQLite 完成。这将在 CPU 时间和内存使用方面更加高效。
  • 尽量避免一次获取所有内容,因为无论您做什么,都有数千条记录意味着大量内存。
  • 除非有非常令人信服的理由,否则不要复制数据。您可以直接使用托管对象,像这样复制数据几乎是不合适的。
  • 由于您似乎在使用集合视图,请考虑使用NSFetchedResultsController. 它旨在与表和集合视图一起使用,并有助于降低内存和 CPU 使用率。