19

I need to get the memory usage (MB) for my app programmatically in Swift. I found sample code for Objective-C. Anyone can help me to convert this into Swift. Thanks!

void report_memory(void) {
  struct task_basic_info info;
  mach_msg_type_number_t size = sizeof(info);
  kern_return_t kerr = task_info(mach_task_self(),
                             TASK_BASIC_INFO,
                             (task_info_t)&info,
                             &size);
  if( kerr == KERN_SUCCESS ) {
    NSLog(@"Memory in use (in bytes): %u", info.resident_size);
  } else {
    NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
  }
}
BB_Dev
  • 461
  • 1
  • 5
  • 11

2 Answers2

13

edit: oops, should have checked for duplicates first, Nate answered this question already here. Though you might want to note the slight difference in my version, which uses withUnsafeMutablePointer.

You’re getting downvoted since people don’t like “How can I convert this Obj-C code to Swift” questions, however in this case it might be fair enough since this this specific conversion is particularly hairy. I think this is working:

func report_memory() {
    var info = task_basic_info()
    var count = mach_msg_type_number_t(sizeofValue(info))/4

    let kerr: kern_return_t = withUnsafeMutablePointer(&info) {

        task_info(mach_task_self_,
            task_flavor_t(TASK_BASIC_INFO),
            task_info_t($0),
            &count)

    }

    if kerr == KERN_SUCCESS {
        println("Memory in use (in bytes): \(info.resident_size)")
    }
    else {
        println("Error with task_info(): " +
            (String.fromCString(mach_error_string(kerr)) ?? "unknown error"))
    }
}

There’s a couple of basic coercions – sizeof returns an Int, but the function call needs a UInt32. Similarly (and slightly more infuriatingly), TASK_BASIC_INFO is an Int32, but the call needs a UInt32.

The nasty part is passing in the third parameter. task_info takes a pointer to multiple different kinds of structs of different sizes depending on what kind of info you want. So you need to get a pointer from your task_basic_info object, then cast it to the specific kind of pointer task_info actually wants (which is, once you wade through all the typedefs, a pointer to UInt32).

The docs for task_info say that the last count parameter is supposed to be a count of the “maximum number of integers in task_info” but when it says integers I guess it means 32-bit integers hence divide by 4.

Community
  • 1
  • 1
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
0

Finally I found a solution. Please see below code snippet.

func report_memory() {
    // constant
    let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))

    // prepare parameters
    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)

    // allocate pointer to mach_task_basic_info
    var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)

    // call task_info - note extra UnsafeMutablePointer(...) call
    let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)

    // get mach_task_basic_info struct out of pointer
    let info = infoPointer.move()

    // deallocate pointer
    infoPointer.dealloc(1)

    // check return value for success / failure
    if kerr == KERN_SUCCESS {
        println("Memory in use (in MB): \(info.resident_size/1000000)")
    } else {
        let errorString = String(CString: mach_error_string(kerr), encoding: NSASCIIStringEncoding)
        println(errorString ?? "Error: couldn't parse error string")
    }    
}
BB_Dev
  • 461
  • 1
  • 5
  • 11
  • 3
    Copying someone else's answer, is not equal to "found a solution" At least give a credit to a person who first posted it: https://stackoverflow.com/a/27559770/5318223 – timbre timbre Jan 26 '21 at 21:03