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 *
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.