2

I am experimenting with NSProgress, and finding a simple loop on a background thread causes memory to grow rapidly:

class Worker {
    var progress:NSProgress?

    func doWork() {
        let numIterations:Int64 = 100000
        let delay:UInt32 = 100

        let progressObj = NSProgress(totalUnitCount: numIterations)
       // progressObj.cancellable = true
        progress = progressObj


        let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT

        dispatch_async(dispatch_get_global_queue(priority, 0)) {
            progressObj.becomeCurrentWithPendingUnitCount(numIterations)
            for i in 0...numIterations {
                progressObj.completedUnitCount = i
                usleep(delay)
            }

            progressObj.resignCurrent()
        }

    }

}

Profiling this with the Allocations instrument shows memory grow to about 20mb over 30 seconds (more if I increase the size of the loop). The allocations are all attributed to _NSProgressFraction.

Is there something obvious I'm overlooking, or is this a bug with NSProgress?

Jon Brooks
  • 2,472
  • 24
  • 32
  • I see the same effect - a lot of tuples being created. The doco does suggest not being too granular, but due to KVO not memory usage. – Michael Feb 13 '16 at 23:25

1 Answers1

4

After a little more experimentation, it looks like the act of setting progressObj.completedUnitCount causes NSProgress to make allocations into the current autorelease pool. I found I can keep the memory from growing by wrapping the loop body in an autorelease pool, like so:

for i in 0...numIterations {
    autoreleasepool {
        progressObj.completedUnitCount = i
        usleep(delay)
    }
}    
Jon Brooks
  • 2,472
  • 24
  • 32