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 related to std::function in the block of setTerminationHandler. How do I address this memory leak issue if I want to keep using std::function as a callback? Thanks a lot!
I ran into something similar today on Xcode 12.3. In my case I had the following function, feeding it C++ lambdas as completion handlers:
So I took your idea and one-upped it by adding an extra innermost C++ lambda to the mix :) On my system this makes the program blow up at runtime when the
std::functiontries to copy itself into that innermost C++ lambda;Thread 3: EXC_BAD_ACCESS (code=2, address=0x7fff872ebd38)0x000000010d9dabbc in std::__1::__function::__value_func<void (id<MTLCommandBuffer>)>::__value_func(void const(&)(id<MTLCommandBuffer>)) at /Applications/Xcode12.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:18080x000000010d9dab1d in std::__1::__function::__value_func<void (id<MTLCommandBuffer>)>::__value_func(void const(&)(id<MTLCommandBuffer>)) at /Applications/Xcode12.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:17990x000000010d9daaed in std::__1::function<void (id<MTLCommandBuffer>)>::function(void const(&)(id<MTLCommandBuffer>)) at /Applications/Xcode12.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:24680x000000010d9d67dd in std::__1::function<void (id<MTLCommandBuffer>)>::function(void const(&)(id<MTLCommandBuffer>)) at /Applications/Xcode12.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:24680x000000010d9d6713 in invocation function for block in MetalGPU::GetCommandBuffer(std::__1::function<void (id<MTLCommandBuffer>)> const&) at <my file path>, resolving to the lineRunCodeInNewCallstack(context, [&finalCommandBuffer, CompletionHandler]() {After experimenting with some possible solutions I also observed some related RAII-managed bookkeeping to be decremented below 0, so there's definitely something fishy going on with object lifetimes.
It seems that Xcode doesn't make a local (deep) copy of the passed in std::function object to be used in the Objective-C block? It probably only makes a copy of the pointer (since it's passed by ref), and when the original goes out of scope and dies we're left with garbage. Making a local copy of the passed-in std::function outside the block (by adding
auto CompletionHandlerCopy = CompletionHandler) and using that in the block indeed makes the problem go away.So my proposed solution is: make it easy on yourself, and don't pass the std::function into your function by const ref but by value instead, so that that needed copy is already made for you by the compiler. I changed mine to:
I don't know if this will help with your memory leak problem though, but it did help with my crash.