objective-cdiskarbitration

DiskRef disappears while enumerating disks in callback method


I have used the DiskArbitration classes from the project Disk-Arbitrator on GitHub in a project I'm working on.

I am mounting/unmounting OS X Installer Disk Images via hdiutil and sometimes the callback method defined for DARegisterDiskDescriptionChangedCallback crashes as the disk list is mutated while enumerating, and then that the diskref that get's passed becomes NULL.

Here is the function that get's called for the callback:

DARegisterDiskDescriptionChangedCallback(session, matching, NULL, DiskDescriptionChangedCallback, (__bridge void *)([NBCDisk class]));

...

void DiskDescriptionChangedCallback(DADiskRef diskRef, CFArrayRef keys, void *context) {
#pragma unused(keys)
    if (context != (__bridge void *)([NBCDisk class])) return;

    NSSet *uniqueDisksCopy = [uniqueDisks copy];
    for ( NBCDisk *disk in uniqueDisksCopy ) {
        if ( CFHash(diskRef) == [disk hash] ) {
            CFDictionaryRef desc = DADiskCopyDescription(diskRef);
            disk.diskDescription = desc;
            CFRelease(desc);

            [[NSNotificationCenter defaultCenter] postNotificationName:DADiskDidChangeNotification object:disk];
        }
    }
}

I solved the problem with being mutated while enumerating by making a copy and enumerate on that.

BUT, this code sometimes crash with: "*** CFHash() called with NULL ***"

And that is because the disk has disappeared and the diskRef probably has been deallocated.

So, I need some tips here. I wanted to try and make a copy of the diskRef, and tried that by doing this:

DADiskRef diskRefCopy = diskRef;

But that did not work either. Is there another way to make a copy? Or is there another way altogether I should approach this.


Solution

  • So, I need some tips here. I wanted to try and make a copy of the diskRef, and tried that by doing this:

    DADiskRef diskRefCopy = diskRef;

    Since you're interacting with Core Foundation; use -

    CFRetain(diskRef);
    

    Also, have a look at following reference in Disk-Arbitrator project, File is Disk.m at line #80:

    disk = CFRetain(diskRef);

    And line #75:

    return [uniqueDisk retain];

    I hope it helps! BTW taking copy is not needed NSSet *uniqueDisksCopy = [uniqueDisks copy];.