Утечка памяти - не знаю, как / где использовать CFRelease () CFSet

Я снова борюсь с утечкой памяти, и мне нужна помощь в ее выяснении. Я знаю (или почти уверен), что проблема в CFSet.

Я предполагаю, что мне нужно выполнить CFRelease (), но я не знаю, как это сделать, поскольку мне также нужно вернуть CFSet в USBDeviceCount (). Любая помощь будет оценена по достоинству! Спасибо!

Вот код (который, похоже, отлично работает! За исключением утечек):

// New USB device has been added (callback function)
static void Handle_DeviceMatchingCallback(void *inContext,
                                          IOReturn inResult,
                                          void *inSender,
                                          IOHIDDeviceRef inIOHIDDeviceRef){

    // Log the device ID & device count
    NSLog(@"\nNew USB device: %p\nDevice count: %ld",
          (void *)inIOHIDDeviceRef,
          USBDeviceCount(inSender));

}

// USB device has been removed (callback function)
static void Handle_DeviceRemovalCallback(void *inContext,
                                         IOReturn inResult,
                                         void *inSender,
                                         IOHIDDeviceRef inIOHIDDeviceRef){

    // Log the device ID & device count
    NSLog(@"\nUSB device removed: %p\nDevice count: %ld",
          (void *)inIOHIDDeviceRef,
          USBDeviceCount(inSender));

}

// Counts the number of devices in the device set (includes all USB devices that match dictionary)
static long USBDeviceCount(IOHIDManagerRef HIDManager)
{

    // The device set includes all USB devices that match our matching dictionary. Fetch it.
    CFSetRef devSet = IOHIDManagerCopyDevices(HIDManager);

    // The devSet will be NULL if there are 0 devices, so only try to count the devices if devSet exists
    if(devSet) return CFSetGetCount(devSet);

    // There were no matching devices (devSet was NULL), so return a count of 0
    return 0;
}

- (void) applicationDidFinishLaunching:(NSNotification *)aNotification {

    // Create an HID Manager
    IOHIDManagerRef HIDManager = IOHIDManagerCreate(kCFAllocatorDefault,
                                                    kIOHIDOptionsTypeNone);

    // Create a Matching Dictionary
    CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(
                                                                 kCFAllocatorDefault,
                                                                 2,
                                                                 &kCFTypeDictionaryKeyCallBacks,
                                                                 &kCFTypeDictionaryValueCallBacks);

    // Specify a device manufacturer in the Matching Dictionary
    CFDictionarySetValue(matchDict,
                         CFSTR(kIOHIDTransportKey),
                         CFSTR("USB"));

    // Register the Matching Dictionary to the HID Manager
    IOHIDManagerSetDeviceMatching(HIDManager, matchDict);


    // Register a callback for USB device detection with the HID Manager
    IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &Handle_DeviceMatchingCallback, NULL);
    // Register a callback fro USB device removal with the HID Manager
    IOHIDManagerRegisterDeviceRemovalCallback(HIDManager, &Handle_DeviceRemovalCallback, NULL);

    // Register the HID Manager on our app’s run loop
    IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);

    // Open the HID Manager
    IOReturn IOReturn = IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone);
    if(IOReturn) NSLog(@"IOHIDManagerOpen failed."); // Couldn't open the HID manager!


    CFSetRef devSet = IOHIDManagerCopyDevices(HIDManager);

    CFRelease(devSet);
    CFRelease(matchDict);

}

утечки

Для полноты картины вот решение, которое мне удалось реализовать с помощью Карла (спасибо, Карл !!):

AppDelegate.h:

- (void) updateConnectedUSBs;
@property(retain) __attribute__((NSObject)) IOHIDManagerRef hidManager;
@property (strong) NSSet *usbDeviceSet;

AppDelegate.m:

// New USB device has been added (callback function)
static void Handle_DeviceMatchingCallback(void *inContext,
                                          IOReturn inResult,
                                          void *inSender,
                                          IOHIDDeviceRef inIOHIDDeviceRef){

    AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    [appDelegate updateConnectedUSBs];

}

// USB device has been removed (callback function)
static void Handle_DeviceRemovalCallback(void *inContext,
                                         IOReturn inResult,
                                         void *inSender,
                                         IOHIDDeviceRef inIOHIDDeviceRef){

    AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    [appDelegate updateConnectedUSBs];

}

- (void) updateConnectedUSBs {

    CFSetRef devSet = IOHIDManagerCopyDevices(_hidManager);
    self.usbDeviceSet = CFBridgingRelease(devSet);
    NSLog(@"%@",self.usbDeviceSet);

}

- (void) applicationDidFinishLaunching:(NSNotification *)aNotification 
{

    _hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);

    // Create a Matching Dictionary
    CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(
                                                                 kCFAllocatorDefault,
                                                                 2,
                                                                 &kCFTypeDictionaryKeyCallBacks,
                                                                 &kCFTypeDictionaryValueCallBacks);

    // Specify a device manufacturer in the Matching Dictionary
    CFDictionarySetValue(matchDict,
                         CFSTR(kIOHIDTransportKey),
                         CFSTR("USB"));

    // Register the Matching Dictionary to the HID Manager
    IOHIDManagerSetDeviceMatching(_hidManager, matchDict);

    // Register a callback for USB device detection with the HID Manager
    IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, &Handle_DeviceMatchingCallback, NULL);
    // Register a callback fro USB device removal with the HID Manager
    IOHIDManagerRegisterDeviceRemovalCallback(_hidManager, &Handle_DeviceRemovalCallback, NULL);

    // Register the HID Manager on our app’s run loop
    IOHIDManagerScheduleWithRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);

    // Open the HID Manager
    IOReturn IOReturn = IOHIDManagerOpen(_hidManager, kIOHIDOptionsTypeNone);
    if(IOReturn) NSLog(@"IOHIDManagerOpen failed."); // Couldn't open the HID manager!

    CFRelease(matchDict);

}

введите здесь описание изображения


person William Gustafson    schedule 13.05.2018    source источник


Ответы (1)


Перед выпуском набора необходимо сохранить счетчик. (Так работали все подклассы NSObject до ARC, с помощью метода -release.)

if (devSet) {
     long count = (long)CFSetGetCount(devSet);
     CFRelease(devSet);
     return count;
}

Если вы можете использовать функцию CFAutorelease (), это еще один способ, который вы можете вызвать сразу после его создания, и он будет собран в конце цикла выполнения. Но использование CFRelease напрямую немного эффективнее, если оно не добавляет сложности коду. Раннее автоматическое освобождение иногда позволяет избежать многократных проверок позже, если есть несколько возвратов.

Я считаю, что вам также нужно CFRelease экземпляр HIDManager.

person Carl Lindberg    schedule 13.05.2018
comment
Карл - Большое спасибо за ваш ответ и объяснение. Ваше предложение действительно значительно уменьшило количество просочившихся объектов. Первоначально я пытался сделать CFRelease(HIDManager); в конце applicationDidFinishLaunching, но выпуск HIDManager привел к тому, что функции обратного вызова никогда не запускались. Когда я выпускаю HIDManager, все утечки памяти прекращаются, насколько я могу судить. Как я могу выпустить HIDManager, но сохранить обратные вызовы при изменении количества USB-устройств, подключенных к Mac? Цель здесь - выполнять действие всякий раз, когда USB-устройство подключается / отключается. - person William Gustafson; 13.05.2018
comment
@ x74353 - Ах, да, если вам нужно сохранить HIDManager в течение всего срока службы приложения, утечка его не составит большого труда. Обычным способом тогда было бы сохранить ссылку в переменной свойства / экземпляра самого делегата приложения, которая сохранит ее как свойство, которое затем будет выпущено в его методе dealloc (который, конечно, никогда не появится в обычных обстоятельствах). После установки свойства вы можете освободить локальную ссылку. Таким образом, вы избежите предупреждений, если когда-нибудь запустите анализатор clang (Продукт- ›Анализировать из меню Xcode), который обычно помечает такие утечки. - person Carl Lindberg; 13.05.2018
comment
Удивительный. Еще раз спасибо, Карл! Я пометил ваш ответ как решение и обновил исходное сообщение новым кодом, включающим ваш ответ и предложения. - person William Gustafson; 13.05.2018