Swift 3 iOS Memory Usage for Monitoring

Hi,


I need help to solve this error. Basically this was working fine in Swift 2.x and Swift 3 broken. Please advise.

Note that below code is almost converted to Swift 3 but with below error.


Error

'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to temporarily view memory as another layout-compatible type.


Error Line: 7

let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)


Here is the code snippet

open class func getMemoryUsed() -> Float {
  let MACH_TASK_BASIC_INFO_COUNT = (MemoryLayout<mach_task_basic_info_data_t>.size / MemoryLayout<natural_t>.size)
  let name = mach_task_self_
  let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)
  var size = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)
  let infoPointer = UnsafeMutablePointer<mach_task_basic_info>.allocate(capacity: 1)
  let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)
  let info = infoPointer.move()
  infoPointer.deallocate(capacity: 1)

  if kerr == KERN_SUCCESS {
    let used_bytes: Float = Float(info.resident_size)
    let total_bytes: Float = Float(ProcessInfo.processInfo.physicalMemory)
    log.info("Used: \(used_bytes / 1024.0 / 1024.0) MB out of \(total_bytes / 1024.0 / 1024.0) MB (\(used_bytes * 100.0 / total_bytes)%)")
    return used_bytes / 1000000 /
  }
}

Replies

Mach, like many old school C-based APIs, plays fast’n’loose with typing. Getting this to work with Swift 3’s stricter aliasing rules is tricky. Honestly, I think you might be better off writing a C helper routine to do this and then calling that helper routine from Swift.

However, to answer your direct question, here’s how to call

task_info
to get
MACH_TASK_BASIC_INFO
in Swift 3.
func mach_task_self() -> task_t {
    return mach_task_self_
}

func getMegabytesUsed() -> Float? { 
    var info = mach_task_basic_info()
    var count = mach_msg_type_number_t(MemoryLayout.size(ofValue: info) / MemoryLayout<integer_t>.size)
    let kerr = withUnsafeMutablePointer(to: &info) { infoPtr in
        return infoPtr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { (machPtr: UnsafeMutablePointer<integer_t>) in
            return task_info(
                mach_task_self(),
                task_flavor_t(MACH_TASK_BASIC_INFO),
                machPtr,
                &count
            )
        }
    }
    guard kerr == KERN_SUCCESS else {
        return nil
    }
    return Float(info.resident_size) / (1024 * 1024) 
}

Notes

  • Lines 1…3 expose

    mach_task_self_
    via a function-based wrapper, as you’d expect in C.
  • Line 6 declares the output buffer on the stack as the correct type,

    mach_task_basic_info
    .
  • Line 7 calculates its size in terms of

    integer_t
    , which is how Mach IPC counts.
  • Line 8 gets its address as an

    UnsafeMutablePointer<mach_task_basic_info>
    .
  • Line 9 reinterprets that as a contiguous array of

    integer_t
    , which is what Mach IPC wants.

Finally, be warned that the value returned by this function has limited utility. It might be useful when debugging but it’s almost certainly a very bad idea to use in production code. If you are using it in product code, please post back explaining what you’re using it for and I can explain why that’s a bad idea (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Eskimo,


Can you expand on your comment that it's a bad idea to use this in production code? I was looking for a way to monitor "typical" memory usage.

Can you expand on your comment that it's a bad idea to use this in production code?

Sure. The problem with this API — and this is true for all the low-level Mach APIs that let you introspect the state of virtual memory — is that they are intimately tied to the kernel’s VM implementation. That has changed radically in the past and may well change radically in the future, and those changes can invalidate the assumptions implicit in your use of the API. I talked about this in detail on this thread.

I was looking for a way to monitor "typical" memory usage.

OK. And what action are you going to take based on the value you get back? If the goal is to report that value via your analytics, that’s fine. When you do your analysis you can take the OS version and device hardware into account. If, on the other hand, you plan to change your app’s behaviour based on that value, you have to be very careful.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Eskimo,


Thank you for your posts so far. It has been very interesting.


I'm currently working on a project in which I would like to destroy a bunch of objects when the memory is high due to performance.

I tried using the code above but does not seem to work for me.


When exiting a screen, we would like to check the main app memory and destroy these objects if the memory is above a certain threshold. From Activity Monitor in instruments and also xCode, I see the memory goes down when these objects are dropped. But when I used the above code to chek the memory programmatically, it doesn't seem to change. With this, I have 2 questions.


1) The memory observed from xCode, Activity Monitor in Instruments and reading from low-level mach API all shows different numbers. Why are all three different and how are they calculated?


2) As discussed, I would like to destroy memory intensive objects when leaving a screen if a memory threshold is reached. Could you suggest the best way to do this? Using the delegate method or listening for notifications when a low memory warning is not an option as we would like to be safe and only remove it when exiting a screen as we don't want to remove things while needing it. Also, we would like to remove these objects way earlier than it hitting low memory warnings. At the same time, removing these objects everytime is not good due to the overhead to create and initalize them again.


Thanks for your help in advance!