Search code examples
c++objective-cmacosmemory-leaksnsobject

ARC doesn't work in Objective-C++


I've got a c++ function that gets a std::map object and convert it to CFMutableDisctionryRef in order to use it on method CFNotificationCenterPostNotification. Here's my implementation :

void IPCNotificationSender::send(const char *identifier, map<const char *, const char *> dict)
{
    NSMutableDictionary *myDict = [NSMutableDictionary dictionary];

    CFStringRef cfIdentifier = CFStringCreateWithCString(NULL, identifier,
                                      kCFStringEncodingMacRoman);
    for (std::map<const char *, const char *>::iterator it=dict.begin(); it!=dict.end(); ++it)
    {
        NSString *key = [NSString stringWithUTF8String:it->first];
        NSString *val = [NSString stringWithUTF8String:it->second];
        myDict[key] = key;
    }
    CFMutableDictionaryRef myCFDict = (CFMutableDictionaryRef)CFBridgingRetain(myDict);

    CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), cfIdentifier, NULL, myCFDict, TRUE);

    CFRelease(myCFDict);
    CFRelease(cfIdentifier);
}

However, there seems to be a memory leak in the NSString *key object where it should be released automatically. I've tried to implement the conversion on top of objective-C function type and still got the same results... I tend to believe that the mixture between c++ and objective-C, although valid, causes some issues with objective-c garbage collector.

Where did I go wrong in my implementation ?

thanks


Solution

  • C++ issues:

    • this map looks bad. it should be map<string, string>
    • you are passing map by value not by const rerence

    Objective C issue:

    Based on clues which gives accepted answer I suspect what is there actual problem. Your C++ code runs continuously without reaching auto release pool. So when you are using Objective C API where auto release pool is involved this objects are not getting released since auto release pool never gets control.

    So I would write this like this:

    NSString *ConvertToObjC(const string& s)
    {
        return [NSString stringWithUTF8String: s.c_str()];
    }
    
    NSDictionary *ConvertToObjC(const map<string, string>& cppMap)
    // here I use templates which do lots of magic, but this is off topic,
    {
        NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity: cppMap.count()];
    
        for (const auto& x : cppMap)
        {
            result[ConvertToObjC(x.first)] = ConvertToObjC(x.second);
        }
    
        return result;
    }
    
    void IPCNotificationSender::send(const string& identifier,
                                     const map<string, string>& cppMap)
    {
        @autoreleasepool {
             auto ident = ConvertToObjC(identifier);
             auto myDic = ConvertToObjC(cppMap);
    
             CFNotificationCenterPostNotification(
                     CFNotificationCenterGetDistributedCenter(), 
                     (CFStringRef)CFBridgingRetain(ident),
                     NULL,
                     (CFDictionaryRef)CFBridgingRetain(myDict),
                     TRUE);
        }
    }