Using CFUUIDRefs as a Dictionary's keys - iphone

I have a bunch of devices.
They each have a UUID that distinguishes them from each other.
Logically, this is the natural thing to key them by in a dictionary tracking them all, then.
However, the [device UUID] method passes back a CFUUIDRef.
First off, this isn't an object.
But hey, we can fix that.
[device_dictionary setObject:device for Key(__bridge id)[device uuid]];
No, wait, that's not a valid key: It doesn't implement the <NSCopying> protocol.
What's more, since I'm casting these CFUUIDRefs into objects on the fly, will the dictionary even realize when it's been passed the same CFUUIDRef twice? Or will the new object created on the fly by the cast not register as the same object?
Care to help me brainstorm on this? How would you key a dictionary with UUIDs, when they were available as non-objects?

The obvious solution would be to convert the returned CFUUIDRef into a string for use as the key in the dictionary.
You can convert the CFUUIDRef to a CFStringRef using CFUUIDCreateString() as follows:
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, [device uuid]);
You can then use toll-free bridging to convert the CFStringRef to an NSString for use in an NSDictionary.
If you need to convert the string representation of the UUID back to a CFUUIDRef you can use CFUUIDCreateFromString() as follows:
CFUUIDRef uuid = CFUUIDCreateFromString(kCFAllocatorDefault, uuidString);
There is also a wrapper for CFUUIDRef which implements NSCoding which can be found at https://gist.github.com/294023. However, this doesn't appear to be ARC compatible.

CFUUIDRef is a CFType, and you can use any CFType as keys in a CFDictionary:
CFMutableDictionaryRef d = CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFUUIDRef u1 = CFUUIDCreateFromString(kCFAllocatorDefault, CFSTR("68753A44-4D6F-1226-9C60-0050E4C00067"));
CFUUIDRef u2 = CFUUIDCreateFromString(kCFAllocatorDefault, CFSTR("68753A44-4D6F-1226-9C60-0050E4C00067"));
CFDictionarySetValue(d, u1, CFSTR("fnord"));
CFShow(CFDictionaryGetValue(d, u2)); // prints "fnord"
Unfortunately toll-free bridging doesn't work properly in this case, as -copy will be called on the key in NSMutableDictionary's -setObject:forKey: before the custom callbacks (see http://www.cocoabuilder.com/archive/cocoa/163407-using-nsimages-as-keys-to-dictionary.html#163439) resulting in a crash. In any case, it is trivial to use CFDictionary in lieu of NSDictionary.
Another option is NSMapTable if you want an Objective-C solution:
NSMapTable *t = NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 0);
NSMapInsert(t, u1, CFSTR("fnord"));
NSLog(#"%#", NSMapGet(t, u2)); // prints "fnord"

Related

A question about NSDictionary initialization

the result is 4 2 3, but what happened in the process of initialization of this NSDictionary?
it's because that its assignment just execute at first time and ignore the rest assignment to same key? Or it's because that its assigment execute with Reverse order?
NSDictionary *dic = #{
#"a":#"4",
#"b":#"2",
#"c":#"3",
#"a":#"1",
#"b":#"5",
#"c":#"6",
};
NSLog(#"luozhiyong,%#",dic[#"a"]);
NSLog(#"luozhiyong,%#",dic[#"b"]);
NSLog(#"luozhiyong,%#",dic[#"c"]);
From the documentation of NSDictionary:
NSDictionary A static collection of objects associated with unique keys.
In addition to the provided initializers, such as initWithObjects:forKeys:, you can create an NSDictionary object using a dictionary literal.
NSDictionary *dictionary = #{
#"anObject" : someObject,
#"helloString" : #"Hello, World!",
#"magicNumber" : #42,
#"aValue" : someValue
};
In Objective-C, the compiler generates code that makes an underlying call to the dictionaryWithObjects:forKeys:count: method.
From the documentation of dictionaryWithObjects:forKeys:count:
This method steps through the objects and keys arrays, creating entries in the new dictionary as it goes.
The result of
NSDictionary *dic = #{
#"a":#"4",
#"b":#"2",
#"c":#"3",
#"a":#"1",
#"b":#"5",
#"c":#"6",
};
is unpredictable and may be different in other versions of Foundation. On macOS 10.13.6 the duplicate keys are ignored.

How to store UUID as int

I m having some issues here.. I am using the following code to generate the uuid in mine application.
- (NSString *)GetUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
This code returns a NSString object back. Now I want to store the generated UUID as unsigned int. Any suggestions please how can i do it here. Thanks.
According to the Wikipedia page, UUIDs are 128 bits long. The largest int value you'll be able to work with is 64 bits. Therefore, I'd says you can't store it in an int because there simply isn't room.
Convert UUID to String using .UUIDString
Take string's .hash property.
Cast to int.
int numericFormUUID = (int)UUIDString.hash;
Note: collision is possible, as always, with hashing. Two users could end up with the same value here, whereas UUID is guaranteed unique.

NSDictionary with integer as number

I have a NSDictionary with the following layout:
{
1:{
... some data ...
}
...
}
I have a NSNumber object with a integer value of 1, but when I do
[my_dict objectForKey:my_number] it returns null.
If I try and convert NSNumber to a integer via [my dict objectForKey:[my_number intValue]] I get a warning and the program crashes when it reaches that part of the code.
Any help would be greatly appreciated.
Keys in a NSDictionary or NSMutableDictionary must be objects, like NSNumber. They cannot be primitive data types, like int.
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
Looks like you're trying to use an integer as the key in your NSDictionary. This would be correct with an NSArray, with an NSDictionary actually needs a proper object as a key.
You might have more success in this particular case feeding that data into an NSArray, and accessing it with:
id *someData = [my_array objectAtIndex:1];

Toll free bridging gotchas

Are there any gotchas for Toll free bridging between NS and CF types?
I'm not sure if I'm doing it wrong but I can't seem to use CF opaque types like ABAddressID inside of an NS Array.
There are not too many 'gotchas'. But this is a C based language, so not every item descends from a CFType. For instance an ABRecordID is really just a 32 bit integer. So its not a CFType. To add ABRecordIDs to an array you would do something like this:
NSMutableArray* newArray = [NSMutableArray array];
ABRecordID someID = 24875247; // you get this somewhere from some call
[newArray addObject:[NSNumber numberWithInt:someID]]; // adds an ABRecordID to the array by putting the int into an NSNumber
Then later when you want the number back:
ABRecordID thatID = [[newArray objectAtIndex:0] intValue]; // retrieve the number, then ask for its int value.
If you read the documentation on a CFType, it will always say whether it is toll free bridged with some NS* counterpart.
Quote from the docs:
"CFNumber is “toll-free bridged” with its Cocoa Foundation counterpart, NSNumber. This means that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object. Therefore, in a method where you see an NSNumber * parameter, you can pass in a CFNumberRef, and in a function where you see a CFNumberRef parameter, you can pass in an NSNumber instance. This fact also applies to concrete subclasses of NSNumber. See Integrating Carbon and Cocoa in Your Application for more information on toll-free bridging."
But an int in C is most definitely NOT a CFNumber.
Hope that helps,
--Tom

Obj-C: Difference between "Fairfield" and #"Fairfield" (with at string)?

I just had a ridonkulous typo in my iPhone app, answered here.
Now I'm wondering about the #"..." notation.
why this works:
NSArray *someArray = [NSArray arrayWithObjects: #"Fairfield", nil];
and this does not (even though it compiles, it will throw an EXC_BAD_ACCESS):
NSArray *someArray = [NSArray arrayWithObjects: "#Fairfield", nil];
Edit:
Ok, so you guys have pointed out that I can't add a C string to an NSArray, because it's obviously not an object.
Now another question: Isn't this somewhat of an oversight? I mean, why does the "...WithObjects:" message specify a list of (id) instead of (NSObject *)?
"#Fairfield" is a normal C string with an '#' character in it. #"Fairfield" is an Objective-C string (NSString on OS X) with no literal '#' in it.
You cannot add C strings to Cocoa collections.
It accepts id rather than NSObject because all initialisers return id. All initialisers return id because subclasses would otherwise override the return type of their ancestors' initialisers.
For example, -[NSMutableString init] can't return NSMutableString * because it subclasses -[NSString init], which can't return NSString * because it overrides -[NSObject init].
Unfortunately, implicit type-casting between const char * and id is perfectly legit, so the compiler won't throw a warning, however a static analyser may be able to pick this sort of mishap up fairly easily.
"Fairfield" is a C string, #"Fairfield" is an Objective-C string.
#"Fairfield" is an object (NSString), so you can send it methods ([#"Fairfield" uppercaseString]) and add it to Objective-C arrays ([NSArray arrayWithObjects:#"Fairfield",nil]). You can only add objects to NSArrays.
On the other hand, "Fairfield" is a C string, and is generally not used in Cocoa. For the most part, you can get by with only using #"Fairfield"
The other reason that a number of things in Cocoa deal with id rather than NSObject* is because, unlike some other languages (say, Java and C#), where all objects in the language must inherit from some global base class, it's entirely possible to have objects that do not descend from NSObject (NSProxy being one example). It's not something you'd do often, but it is possible. The id type means "pointer to any Objective C instance".