2016-08-22 5 views
0

私の以前のバージョンのアプリケーションでは、すべてのユーザーデータを(NSKeyedArchiverを使って).datファイルに保存しましたが、新しいバージョンでは、 。大量のデータをマイグレーションする際のメモリの問題を解決する

私はこのデータをすべてレルムにインポートしようとしています(それはたくさんあります)。しかし、それはあまりにも多くのメモリを取っているので、移行が完了する前にデバッガが最終的に私のアプリを殺すことになります。 「奇妙なこと」は、ハードディスクのデータがわずか1.5MBだが、1GB以上のメモリを使用しているため、何か間違っている。

私も複数のスレッドで作業しようとしましたが、それは役に立ちませんでした。さて、マイグレーションプロセスを高速化しました(これは良いですが)、同じ量のメモリが必要でした。

誰が私を助けることができますか? autoreleasepoolはトリックをした、

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() 
+0

ループ内に多数のオブジェクトを割り当てています。ループ内で割り当てているオブジェクトの存続期間を制限するために、内部ループの本体を[autorelease pool](http://en.swifter.tips/autoreleasepool/)でラップすることをお勧めします。私はそれがあなたの問題に完全に対処するかどうかは分かりませんが、助けてください! – bdash

+0

ここには、レルム固有のコードはありません。 Realmインスタンスはどこで開かれていますか、どこで 'autoreleasepool {}'にラップされていますか? – EpicPandaForce

+0

@bdashありがとう、私はそれを見てみましょう。 autoreleasepoolについてはまだ経験がありません。 – NielsKoole

答えて

1

が@bdashによって示唆されたここで見つけることができます。..詳細については、以下の私のコードを参照してください。

私は大規模な中央ディスパッチの構文的な砂糖としてAsyncSwiftを使用しますが、ブロックグループを使用すると、グループはメモリが解放されなかったブロックへの参照を保持します。私は今でもまだグループを利用していますが、グループが終了したら退室します。

私は以下のコード例を提供して、明確にしました。複数のスレッドを使用すると、メモリが150MBを超えないうちに、(10倍速く)よりパフォーマンスが向上しました。以前は、メモリ圧迫のためにアプリが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() 
関連する問題