A memory leak when capturing callback function from block of setTerminationHandler of NSTask

Hi @eskimo and experts,

I created a simple code and found there is a memory leak:

#import <Foundation/Foundation.h>
#include <functional>

void testLeaks(const std::function <void (int)>& inCallback) {
  NSTask* task = [[NSTask alloc] init];
  [task setTerminationHandler:^(NSTask* inTask) {
    inCallback(inTask.terminationStatus);
  }];
   
  [task release];
}
int main(int argc, const char * argv[]) {
  auto callback = [](int result) {
    NSLog(@"callback");
  };
  testLeaks(callback);
  NSLog(@"done");
  return 0;
}

The output of leaks:

Process:         testLeaks2 [13084]
Path:            /Users/USER/Library/Developer/Xcode/DerivedData/Build/Products/Debug/testLeaks2
Load Address:    0x10a04d000
Identifier:      testLeaks2
Version:         ???
Code Type:       X86-64
Platform:        macOS
Parent Process:  leaks [13083]

Date/Time:       2022-02-28 23:34:28.374 +0800
Launch Time:     2022-02-28 23:34:27.939 +0800
OS Version:      Mac OS X 10.15.6 (19G73)
Report Version:  7
Analysis Tool:   /Applications/Xcode.app/Contents/Developer/usr/bin/leaks
Analysis Tool Version:  Xcode 12.4 (12D4e)
----

leaks Report Version: 4.0
Process 13084: 596 nodes malloced for 59 KB
Process 13084: 1 leak for 48 total leaked bytes.

    1 (48 bytes) ROOT LEAK: <__NSMallocBlock__ 0x7fbbc2704350> [48]  testLeaks2  invocation function for block in testLeaks(std::__1::function<void (int)> const&)  0..."

Looks the issue is in the block of setTerminationHandler. How do I address this memory leak issue if I want to keep using std::function as a callback?

Accepted Reply

just found if any variable even primitive types captured by the block of -setTerminationHandler, then there is a leak

Well, that makes it quite clear that this is a bug, and I’d appreciate you filing it as such.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

just found if any variable even primitive types captured by the block of setTerminationHandler, then there is a leak:

    void testLeaks() {
         int abc = 123;
        NSTask* task = [[NSTask alloc] init];
        [task setTerminationHandler:^(NSTask* inTask) {
            NSLog(@"setTerminationHandler %d %d", inTask.terminationStatus, abc);
        }];
        [task release];
    }

The output is the same:

Process 10635: 593 nodes malloced for 59 KB
Process 10635: 1 leak for 48 total leaked bytes.
    1 (48 bytes) ROOT LEAK: <__NSMallocBlock__ 0x7f935ed09690> [48]  testLeaks2  invocation function for block in testLeaks()  0x10a6a27e0

just found if any variable even primitive types captured by the block of -setTerminationHandler, then there is a leak

Well, that makes it quite clear that this is a bug, and I’d appreciate you filing it as such.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks a lot, @eskimo. Please refer to FB9942875.

  • Ta!

  • @eskimo, more findings, if NSTask is launched successfully, then there is no leak. The leak happens when failed to launch task or not to launch task.

  • Yeah, kinda figured that, but it still shouldn't leak (-:

Add a Comment