objective-cnsarraycfstringdiskarbitration

Convert NSArray to CFStringRef *


I need a way to convert an NSArray to a null terminated list compatible with the arguments option of DADiskMountWithArguments.

The documentation specifies the argument option to be a "Null terminated list" of type CFStringRef arguments[].

I have created a Mount method that I want to pass an NSArray with the arguments, and in my method I need to convert the NSArray to a CFStringRef *.

I've tried myself but I always get in trouble with ARC, and I have not been able to find any good way to do this yet.

I've looked at the project Disk-Arbitrator in GitHub https://github.com/aburgh/Disk-Arbitrator/blob/master/Source/Disk.m for inspiration, and the creator of that application uses this method:

- (void)mountAtPath:(NSString *)path withArguments:(NSArray *)args
{
    NSAssert(self.isMountable, @"Disk isn't mountable.");
    NSAssert(self.isMounted == NO, @"Disk is already mounted.");

    self.isMounting = YES;

    Log(LOG_DEBUG, @"%s mount %@ at mountpoint: %@ arguments: %@", __func__, BSDName, path, args.description);

    // ensure arg list is NULL terminated
    id *argv = calloc(args.count + 1, sizeof(id));

    [args getObjects:argv range:NSMakeRange(0, args.count)];

    NSURL *url = path ? [NSURL fileURLWithPath:path.stringByExpandingTildeInPath] : NULL;

    DADiskMountWithArguments((DADiskRef) disk, (CFURLRef) url, kDADiskMountOptionDefault,
                         DiskMountCallback, self, (CFStringRef *)argv);

    free(argv);
}

But that is not allowed in ARC, and I can't find a way to do it.

Update for better clarity:

This line:

id *argv = calloc(args.count + 1, sizeof(id));

Gives the following error message:

Implicit conversion of a non-Objective-C pointer type 'void *' to '__strong id *' is disallowed with ARC

Pointer to non-const type 'id' with no explicit ownership.

To fix that i try to do this:

id argv = (__bridge id)(calloc(args.count + 1, sizeof(id)));

Then this line:

[args getObjects:argv range:NSMakeRange(0, args.count)];

Gives the following errors:

[ERROR] Implicit conversion of an Objective-C pointer to '__unsafe_unretained id *' is disallowed with ARC

[WARN] Incompatible pointer types sending '__string id' to parameter of type '__unsafe_unretained id *'

The declaration of -getObjects:range: look like this:

- (void)getObjects:(id [])aBuffer range:(NSRange)aRange

So from the error message i got I assume i have to pass an '__unsafe_unretained id *' to 'getObjects:(id [])aBuffer'. So to fix that i declare my id as __unsafe_unretained like this:

__unsafe_unretained id argv = (__bridge __unsafe_unretained id)(calloc(args.count + 1, sizeof(id)));

And update this line like this:

[args getObjects:&argv range:NSMakeRange(0, args.count)];

Now i don't have any errors there, but in the call to DADiskMountWithArguments i get the following error:

Cast of an Objective-C pointer to 'CFStringRef *' (aka 'const struct __CFString **) is disallowed with ARC

So here I got stuck as I have not been able to fix this error, and I don't know if I made mistakes earlier or if I haven't found the right way send the CFStringRef, therefore I decided to ask for guidance here.

This is how it looks in context, where args is an NSArray declared earlier:

__unsafe_unretained id argv = (__bridge __unsafe_unretained id)(calloc(args.count + 1, sizeof(id)));

[args getObjects:&argv range:NSMakeRange(0, args.count)];

DADiskMountWithArguments((DADiskRef) disk, (__bridge CFURLRef) url, kDADiskMountOptionDefault, NULL, (__bridge void *)self, (CFStringRef *)argv );

So my question is either, how can this method be made ARC-friendly, or is there another/better way to get from an NSArray to a NULL-terminated CFStringRef *


Solution

  • Try this:

    CFStringRef *argv = calloc(args.count + 1, sizeof(CFStringRef));
    CFArrayGetValues((__bridge CFArrayRef)args, CFRangeMake(0, args.count), (const void **)argv );
    DADiskMountWithArguments((DADiskRef) disk, (CFURLRef) url, kDADiskMountOptionDefault,
                         DiskMountCallback, self, argv);
    free(argv);
    

    There are no Core Foundation/Cocoa memory management issues because CFArrayGetValues() doesn't give you ownership of the returned values.