通过迁移大量数据解决内存问题

时间:2016-08-22 20:03:20

标签: ios swift memory realm

在我以前的应用程序版本中,我将所有用户数据存储到.dat文件中(使用NSKeyedArchiver),但在我的新版本中,我想升级到真实的(m)数据库。

我正在尝试将所有这些数据(可能很多)导入Realm。但它占用了大量内存,调试器最终会在迁移完成之前终止我的应用程序。 “奇怪”的是,硬盘上的数据只有1.5 MB大,但它占用的内存超过1GB,所以我做错了。

我也尝试使用多个线程,但这没有帮助。好吧,它加快了迁移过程(这很好),但它也占用了相同的内存量。

谁能帮助我?有关详细信息,请参阅下面的代码..

FYI异步可以在https://github.com/duemunk/Async

找到
import Async

let startDate = NSDate(timeIntervalSince1970: 1388534400).startOfDay // Start from 2014 jan 1st
let endDate = NSDate().dateByAddingTimeInterval(172800).startOfDay // 2 days = 3600 * 24 * 2 = 172.800

var pathDate = startDate
let calendar = NSCalendar.currentCalendar()

let group = AsyncGroup()
var allPaths = [(Int, Int)]()

while calendar.compareDate(pathDate, toDate: endDate, toUnitGranularity: .Month) != .OrderedDescending {

    // Components
    let currentMonth = calendar.component(.Month, fromDate: pathDate)
    let currentYear = calendar.component(.Year, fromDate: pathDate)
    allPaths.append((currentYear, currentMonth))

    // Advance by one month
    pathDate = calendar.dateByAddingUnit(.Month, value: 1, toDate: pathDate, options: [])!
}

for path in allPaths {
    group.background {

        // Prepare path
        let currentYear = path.0
        let currentMonth = path.1
        let path = (Path.Documents as NSString).stringByAppendingPathComponent("Stats_\(currentMonth)_\(currentYear).dat")
        print(path)

        if NSFileManager.defaultManager().fileExistsAtPath(path) {
            NSKeyedUnarchiver.setClass(_OldStatisticsDataModel.self, forClassName: "StatisticsDataModel")

            if let statistics = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? [_OldStatisticsDataModel] {
                // Loop through days
                for i in 1...31 {
                    let dateComponents = NSDateComponents()
                    dateComponents.year = currentYear
                    dateComponents.month = currentMonth
                    dateComponents.day = i
                    dateComponents.hour = 0
                    dateComponents.minute = 0

                    // Create date from components
                    let userCalendar = NSCalendar.currentCalendar() // user calendar

                    guard let date = userCalendar.dateFromComponents(dateComponents) else {
                        continue
                    }

                    // Search for order items
                    let filtered = statistics.filter {
                        if let date = $0.date {
                            let dateSince1970 = date.timeIntervalSince1970
                            return date.startOfDay.timeIntervalSince1970 <= dateSince1970 && date.endOfDay.timeIntervalSince1970 >= dateSince1970
                        }

                        return false
                    }

                    if filtered.isEmpty == false {
                        // Create order
                        let transaction = Transaction()
                        transaction.employee = Account.API().administratorEmployee()

                        let order = Order()
                        order.status = PayableStatus.Paid
                        order.createdDate = date.timeIntervalSince1970
                        order.paidDate = date.timeIntervalSince1970

                        // Loop through all found items
                        for item in filtered {
                            // Values
                            let price = (item.price?.doubleValue ?? 0.0) * 100.0
                            let tax = (item.tax?.doubleValue ?? 0.0) * 100.0

                            // Update transaction
                            transaction.amount += Int(price)

                            // Prepare order item
                            let orderItem = OrderItemm()
                            orderItem.amount = item.amount
                            orderItem.price = Int(price)
                            orderItem.taxPercentage = Int(tax)
                            orderItem.name = item.name ?? ""
                            orderItem.product = Product.API().productForName(orderItem.name, price: orderItem.price, tax: orderItem.taxPercentage)

                            // Add order item to order
                            order.orderItems.append(orderItem)
                        }

                        if order.orderItems.isEmpty == false {
                            print("\(date): \(order.orderItems.count) order items")

                            // Set transaction for order
                            order.transactions.append(transaction)

                            // Save the order
                            Order.API().saveOrders([order])
                        }
                    }
                }
            }
        }
    }
}

group.wait()

1 个答案:

答案 0 :(得分:1)

正如@bdash提出的问题评论一样,autoreleasepool做了这个伎俩。

我使用AsyncSwift作为大型中央调度的语法糖,但是当使用块组时,该组保留对该块的引用,这导致内存未被释放。我现在仍然使用一个小组,但是在小组结束后离开小组。

我在下面提供了我的代码示例,以使事情更加清晰。使用多个线程给我带来了令人难以置信的(比快10倍)更高的性能,而内存不会超过150MB。由于内存压力,之前的应用程序崩溃了1,3GB。

let group = AsyncGroup()
var allPaths = [(Int, Int)]()

// Some logic to fill the paths -> not interesting

for path in allPaths {
    group.enter() // The following block will be added to the group
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        autoreleasepool { // Creating an autoreleasepool to free up memory for the loaded statistics

            // Stripped unnecessary stuff

            if NSFileManager.defaultManager().fileExistsAtPath(path) {

                // Load the statistics from .dat files
                NSKeyedUnarchiver.setClass(_OldStatisticsDataModel.self, forClassName: "StatisticsDataModel")
                if let statistics = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? [_OldStatisticsDataModel] {

                    // Loop through days
                    for i in 1...31 {
                        autoreleasepool {
                            // Do the heavy stuff here
                        }
                    }
                }
            }
        }

        group.leave() // Block has finished, now leave the group
    }
}

group.wait()