Pointer casting with ARC - objective-c

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;

Related

Signed byte array to UIImage

I am trying to display a picture from a byte-array produced by a web service. Printing out a description it looks like this:
("-119",80,78,71,13,10,26,10,0,0,0,13,3 ... )
From the header it is clear that it's a png encoded in signed integers. It is an __NSCFArray having __NSCFNumber elements.
My code in Objective-C (based on much googling):
NSData *data = [NSData dataWithBytes:(const void *)myImageArray length [myImageArray count]];
UIImage *arrayImage = [UIImage imageWithData:data];
I receive a null UIImage pointer.
I also tried to converting it to unsigned NSNumbers first and then passing it to NSData, though perhaps I did not do this correctly. What am I doing wrong?
You cannot simply cast an NSArray of NSNumber into binary data. Both NSArray and NSNumber are objects; they have their own headers and internal structure that is not the same as the original string of bytes. You'll need to convert it byte-by-byte with something along these lines:
NSArray *bytes = #[#1, #2, #3];
NSMutableData *data = [NSMutableData dataWithLength:bytes.count];
for (NSUInteger i = 0; i < bytes.count; i++) {
char value = [bytes[i] charValue];
[data replaceBytesInRange:NSMakeRange(i, 1) withBytes:&value];
}
char is a signed int8_t, which appears to be the kind of data you're working with. It is often used to mean "an ASCII character," but in C it is commonly also used to mean "byte."

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

CFStringRef seems to hold on to NSString with no strong reference with just __bridge (ARC)

In ARC, __bridge is supposed to imply just a cast with no ownership transfer. But the following code snippet does not crash:
int i = 8;
NSString* str = [[NSString alloc] initWithFormat:#"abc%d",i];
Employee* e = [Employee newEmployee];
CFStringRef cfStr = (__bridge CFStringRef)(str);
str = nil;
printf("%s\n",CFStringGetCStringPtr(cfStr, kCFStringEncodingMacRoman));
When I assign nil to str, the string should ideally be deallocated and the printf line should crash.
Quoting Josh's comment:
"The memory just hasn't been reused yet. If you turn on malloc scribble or guard, you'll get your crash."

warning: Semantic Issue: Incompatible pointer types initializing 'char *' with an expression of type 'NSString *'

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?

iOS - How do I create a read and write stream to a network socket?

I am new to iOS development. I am attempting to create a read and write stream. I am using the CFNetworking programming guide's examples to try and get something working.
I am trying to schedule the read stream on the run loop to work around the issue of the streams blocking. Right away I have run into issues. How can I create a CFHost object using CFHhostCreateWithAddress? Here is what I have so far:
NSString *address = #"irc.ubuntu.net";
CFDataRef addressDataRef = (CFDataRef)[address dataUsingEncoding:NSASCIIStringEncoding];
CFHostRef host = CFHostCreateWithAddress(kCFAllocatorDefault, addressDataRef);
//Create Read and Write Stream
CFStreamCreatePairWithSocketToCFHost(kCFAllocatorDefault, host, 8008, &readStream, &writeStream);
The second line bombs. Can someone please tell me how to create a CFHostRef?
Thanks a lot!
The documentation states that the second argument to CFHostCreateWithAddress() must be "A CFDataRef object containing a sockaddr structure for the address of the host. This value must not be NULL."
You're passing a CFDataRef representing "irc.ubuntu.net", which is by no means a sockaddr struct.
Use CFHostCreateWithName:
CFHostRef CFHostCreateWithName (
CFAllocatorRef allocator,
CFStringRef hostname
);
As you probably know, you can cast an NSString * to CFStringRef, or create a constant CFStringRef with the macro CFSTR().
+ (NSData *)dataForIPAddress:(NSString *)address {
struct addrinfo hints;
struct addrinfo *result = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
int errorStatus = getaddrinfo([address cStringUsingEncoding:NSASCIIStringEncoding], NULL, &hints, &result);
if (errorStatus != 0) return nil;
CFDataRef addressRef = CFDataCreate(NULL, (UInt8 *)result->ai_addr, result->ai_addrlen);
if (addressRef == nil) return nil;
freeaddrinfo(result);
return [(NSData *)addressRef autorelease];
}