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.
Related
I'm just trying to cast NSString* to CTStringRef*
NSString *foobar = #"foobar";
CFStringRef *tmp = (__bridge_retained CFStringRef*)foobar;
Can someone help with this error?
"Incompatible types casting 'NSString *' to 'CTStringRef *' (aka const struct __CFString **)with a __bridge_retained cast"
I've tried with simply __bridge and it don't work either. From the documentation, I think the _retained is the right type I need.
Thanks.
If you look closely at the error message you will see what your problem is. The hint is in this part -
__CFString **
Notice the two * - This means that you are trying to cast to a pointer to a pointer, or in other words a reference to a reference. CTStringRef is already a reference, as implied by the 'Ref' part of the name, so you don't need the * in (__bridge_retained CFStringRef*)
Your code should read
NSString *foobar = #"foobar";
CFStringRef tmp = (__bridge_retained CFStringRef)foobar;
Heey
When I'm trying to return a array I'm always getting this strange "waring" message but it does not interrupt my App
Returning 'ABRecordRef' (aka 'const void *') from a function with result type 'ABRecordRef ' (aka 'const void *') discards qualifiers
Here is my code where I'm getting this message
- (ABRecordRef *) findContactsContainingName: (NSString *) fname
{
//TODO: add lastname, phonenumber etc.
// Load the contacts
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
NSArray *thePeople = (__bridge NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
for (id person in thePeople){
NSString *firstname = (__bridge NSString*) ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonFirstNameProperty);
if([firstname isEqualToString: fname]){
return (__bridge ABRecordRef)(person);
}
}
return NULL;
}
Can someone please explain me why I get here a Waring ..
Thanks for help and fast answer
Remove the * here:
- (ABRecordRef *) findContactsContainingName: (NSString *) fname
^
ABRecordRef is already defined as a pointer.
ABRecord is C API and it work in CoreFoundation ways.
In CoreFoundation (and AddressBook) objects are implemented as C structs, and pointers are used to reference them. A string in CoreFoundation is CFStringRef, which is interchangeable (or rather, toll-free bridged) with Foundation object, NSString *. (i.e. the "Ref" in CFStringRef implied a * in it - think it as CFString *, or rather struct __CFString *)
Similarly, ABRecordRef is ABRecord * and hence your return type, ABRecordRef * is actually ABRecord **, a secondary pointer. This is what the compiler is complaining.
You can check out the source code of GNUstep CoreBase and you will find out why. GNUstep is a open-source clone of Cocoa (it predates Cocoa!) for Linux and studying its source code can be very helpful on understanding how Cocoa work under the hood.
I am trying to do the following:
NSString *personDesc = [NSString stringWithFormat:#"Person named %#", person.name];
char *myArguments[] = { personDesc, NULL };
But it is producing this error:
warning: Semantic Issue: Incompatible pointer types initializing 'char *' with an expression of type 'NSString *'
The reason I am trying to convert the NSString into a char is because I am passing myArguments into AuthorizationExecuteWithPrivileges
e.g.
AuthorizationExecuteWithPrivileges(auth, tool, kAuthorizationFlagDefaults, myArguments, NULL);
Any ideas?
As matt said above, you need to convert the NSString to a char* before you can do what you want. Try something like:
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
NSString *personDesc = [NSString stringWithFormat:#"Person named %#", person.name];
const char *cPersonDesc = [personDesc cStringUsingEncoding:stringEncoding];
char *myArguments[] = { cPersonDesc, NULL };
// auth and tool already exist
AuthorizationExecuteWithPrivileges(auth, tool, kAuthorizationFlagDefaults, myArguments, NULL);
It's right. NSString is not char*. It is NSString. They have nothing to do with one another.
Since you give no indication of what you're really trying to do, no further advice can be given. You could convert NSString to char* if you wanted to, e.g. with getCString:maxLength:encoding:. But why would you want to?
ARC is giving me a hard time with following cast:
NSDictionary *attributes;
SecItemCopyMatching((__bridge CFDictionaryRef)keychainItemQuery, (CFTypeRef *)&attributes);
Error: Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC
The problem is that attributes shouldn't be a dictionary, it should be a SecKeyRef or CFDataRef. And then cast that back into NSData for the password data copied into it.
Like so:
CFDataRef attributes;
SecItemCopyMatching((__bridge CFDictionaryRef)keychainItemQuery, (CFTypeRef *)&attributes);
NSData *passDat = (__bridge_transfer NSData *)attributes;
As we were doing something similar things and using the example above, we were facing another problem:
CFDataRef resultRef;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary,
(CFTypeRef *)&resultRef);
NSData* result = (__bridge_transfer NSData*)resultRef;
This will result in an EXEC_BAD_ACCESS, because resultRef is not set to any adress and points somewhere to the memory.
CFDataRef resultRef = nil;
This will fix the error.
Need to change attributes to &attributes
CFDataRef attributes;
SecItemCopyMatching((__bridge CFDictionaryRef) keychainItemQuery, ( CFTypeRef*) &attributes);
NSData* passDat=(__bridge_transfer NSData*) attributes;
In order to print out something in file, I have the following code.
FILE *fp = fopen(cString, "w+");
NSString* message = [NSString stringWithFormat:#":SLEEP: %#:%#\n", ...];
char* cMessage = [message UTF8String]; <-- warning
fprintf(fp, cMessage); <-- warning
fclose(fp);
However, I got Initialization discards qualifiers from pointer target type error in char* cMessage, and Format not a string literal and no format argument warning.
What's wrong with the code?
-UTF8String returns a const char *, but you're assigning it into a char *. As such, you're discarding the const qualifier.
As for fprintf, you should probably be doing:
fprintf(fp, "%s", cMessage);