Objective-C at sign and curly braces, #{ ... } what does it mean? - objective-c

I have this line in Objective-C.
NSMutableArray *mutableArray;
[mutableArray addObject:#{ #"Something" : aObject, #"Otherthing" : anotherObject }];
What does the #{ ... } part do exactly? It is an object, but it seems to create some kind of key, value pair on the fly.

It is creating NSDictionary object as you said. Syntax is simple
NSDictionary* dictionary = #{key: object, key: object};
In your example, keys are objects of NSString class. It is important to remember that dictionary copies keys and retains values.

These are called Literals. Apple LLVM Compiler 4.0 and above can use this.
In your question, the expression creates a dictionary
NSDictionary *settings = #{ AVEncoderAudioQualityKey : #(AVAudioQualityMax) };
Similarly arrays which were created using NSArray arrayWithArray and other similar methods, can now be done easily
NSArray *array = #[ #"Hello", #"World"];
and you will not even need the nil sentinel.
More details here: http://clang.llvm.org/docs/ObjectiveCLiterals.html

The #{ ... } syntax is a shorthand way of creating a NSDictionary introduced as part of Modern Objective-C. The syntax #{#"key1": object1, #"key2": object2} is just a shorthand for more verbose methods like [NSDictionary dictionaryWithObjectsAndKeys:] among a few others.

Related

CFPropertyListCreateDeepCopy fails to process array / dictionary containing NSNull

For some reason this sample code works:
NSArray *immutable = #[ #"a", #"b", #"c" ];
NSMutableArray *mutable = (__bridge id)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFArrayRef)immutable, kCFPropertyListMutableContainers);
and this code produces nil as a result of the conversion:
NSArray *immutable = #[ #"a", [NSNull null], #"c" ];
NSMutableArray *mutable = (__bridge id)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFArrayRef)immutable, kCFPropertyListMutableContainers);
I tried to find any mention of NSNull not being allowed when using this function. I have a suspicion that it has something to do with the way method examines whether property is mutable or not, but I can't really back that up with facts.
Any ideas?
As kind people from apple developer forum pointed out the issue is that Property List Structure is rather strict about data types it can work with. NSNull is not one of allowed ones.
From apple docs:
Property lists are constructed from the basic Core Foundation types CFString, CFNumber, CFBoolean, CFDate, and CFData.

Easiest way to create objects in ios

Does iOS support something like
s = { x:3,y:5 , o:{x:0,y:1}}
like in Javascript?
Or what is the best way to use something like this in iOS?
A literal translation would be :
NSDictionary *s = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:3], #"x",
[NSNumber numberWithInt:5], #"y",
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], #"x",
[NSNumber numberWithInt:1], #"y",
nil ], #"o",
nil ];
But literal translations, even in programming languages, are not best. It all depends on the context and what you need to accomplish. There's probably less code required depending on what you need.
EDIT: There's now a new literal syntax allowing to shrink this code further:
NSDictionary *s = #{ #"x": #3
, #"y": #5
, #"o": #{ #"x": #0, #"y": #1}
};
This syntax implicitely creates the NSDictionary & NSNumber objects.
No, there is no shortcut syntax for dicts in Objective-C. You have to do it manually.
It depends exactly what type of object you mean. There really isn't a generic answer here.
If you're referring to an NSDictionary for example, you could use
+ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys
or any of the variants documented on the same page.
You could write a setter or helper method that could take a string in the above format, syntax check it, parse it, and use the string fields to create or modify an NSDictionary for you automatically from a one line call.
But it's not a built function of Obj C or the NSFoundation frameworks. Some JSON Category additions might do something like this though.
What you can do is create a JSON string and then use a JSON framework to generate the corresponding NSArray/NSDictionary structure. This would approach what you are trying to do, but not quite there... Then you can describe your object structure with something like this:
{"x": 3, "y" : 5, "o" : {"x": 0, "y" : 1}}
Main limitation is that you won't have numeric types out of the box, but you can use NSNumber, NSScanner and so on to get them.
No, it does not support. But i am sorry, im not fammiliar with JS.
To creat a obj in iOS you do the following:
ObjName *objVar;
then you set its properties:
#property (nonatomic, strong) ObjName *objVar;
then you syntesize it in the .m
#syntesize objVar;
and the you alloc it and init it in the desired method.

How does NSDictionary handle NIL objects?

Consider the code below. In essence, we get 2 strings, then we add these values to the NSDictionary.
However, i hit a weird bug. When fbAccessTokenKey is 0x0 (or nil), then twitterToken would not be added as well.
NSString *fbAccessTokenKey=[[UserStockInfo sharedUserStockInfo] getFBAccessTokenKey];
NSString *twitterToken=[[UserStockInfo sharedUserStockInfo] getTwitterAccessTokenKey];
NSDictionary *params= [[NSDictionary alloc] initWithObjectsAndKeys:
fbAccessTokenKey, #"fb_access_token",
twitterToken, #"twitter_access_token",
nil
];
Why is this happening, and what is a good way of resolving this?
nil is used as a 'sentinel' for marking the "end of arguments" list. If twitterToken was nil, the runtime would go through your arguments, and once it got to twitterToken, it would think that it was up to the end of your list of objects and keys. This is due to the way that C/Obj-C is implemented when it comes to list arguments.
The alternative safe way to do it is to use an NSMutableDictionary, and check to see if your values are non-nil, then add them to the mutable dictionary like this:
NSString *fbAccessTokenKey = [[UserStockInfo sharedUserStockInfo] getFBAccessTokenKey];
NSString *twitterToken = [[UserStockInfo sharedUserStockInfo] getTwitterAccessTokenKey];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
if (fbAccessTokenKey) [params setObject:fbAccessTokenKey forKey:#"fb_access_token"];
if (twitterToken) [params setObject:twitterToken forKey:#"twitter_access_token"];
For more technical info, there's a good article on Cocoa with Love: http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
You can use the NSNull object.
Documentation here:
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSNull_Class/Reference/Reference.html
Rather than initializing with initWithObjectAndKeys. Why not instantiate an NSMutableDictionary and then add the key value pairs (or not if the key is null)?
NSMutableDictionary * params = [[NSMutableDictionary alloc] init];
if (fbAccessTokenKey)
[params setObject:fbAccessTokenKey forKey:#"fb_access_token];
// ... etc
You could cast it back to an NSDictionary later if you want to keep it immutable from that point.
Update
Just a note in response to Josh's comment, I should clarify that of course the cast will not magically convert the params NSMutableDictionary to an NSDictionary. But if you are passing it to code which requires an NSDictionary, the cast will let you treat it as such.
Josh's comment includes this code:
NSMutableDictionary * md = [[NSMutableDictionary alloc] init];
NSDictionary * d = (NSDictionary *)md;
[d setObject:#"Yes I am" forKey:#"Still mutable?"];
NSLog(#"%#", d); // Prints { "Still mutable?" = Yes I am; }
This will generate the following compiler warning (and for me, with warnings generating errors, a compile error):
file:blah.m: error: Semantic Issue: 'NSDictionary' may not respond to 'setObject:forKey:'

What does forKey mean? What does it do?

I'm guessing that it gives the object that is being added to the NSMutableDictionary or NSDictionary a name to access it. But, I have to confirm it. Can anybody tell me?
Dictionaries are data structures that contain key-value pairs. They're also known as hash tables. So yes, you use a key to refer to its corresponding value.
For the following dictionary:
// Pseudo-code, not actual Objective-C code, merely for illustration
// (This {} syntax would be really nice to have though...)
NSDictionary *dict = {
#"one" => NSNumber (1),
#"two" => NSNumber (2)
};
The following code yields 1:
NSNumber *one = [dict objectForKey:#"one"];
NSLog(#"%d", [one intValue]);

Cocoa: Dictionary with enum keys?

I need to create a dictionary/hashmap where the
Keys are enums
Values are some subclass of NSObject
NSDictionary won't work here (enums don't conform to NSCopying).
I could perhaps use a CFDictionaryRef here, but I'd like to know if is there any other way to achieve this.
Since enums are integers, you can wrap the enum in an NSNumber. When you add/retreive something to/from the map, you pass the enum to the NSNumber constructor...
Assuming you've got an enum like...
enum ETest {
FOO, BAR
};
You can use it in an NSDictionary like this...
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject: #"Foo!" forKey:[NSNumber numberWithInt: FOO]];
NSLog(#"getting value for FOO -> %#",
[dict objectForKey: [NSNumber numberWithInt: FOO]]);
[dict release];
With VoidPointer's suggestion, it may be better to use NSValue for those times when enums turn out not to be integers (such as when -fshort-enums is in play, which should be never as you'd probably break compatibility with Foundation).
NSValue *value = [NSValue value: &myEnum withObjCType: #encode(enum ETest)];
That's not going to add much here but gives you the general "I want to use <name of non-ObjC type> in a collection class" technique.
Notice that with modern compilers you can tell enums to use a fixed underlying type. This means you can control what storage is used for the enum, but as the above solution is general it still applies even when you know this.
Further extending on the suggestion from Graham Lee...
You could use an objective-c category in order to add a method to NSMutableDictionary that allows you to add a value with a key of your non NSObject type. This keeps your code free from the wrapping/unwrapping syntax.
Again, assuming
enum ETest { FOO, BAR };
First, we're adding a convince constructor to NSValue:
#interface NSValue (valueWithETest)
+(NSValue*) valueWithETest:(enum ETest)etest;
#end
#implementation NSValue (valueWithETest)
+(NSValue*) valueWithETest:(enum ETest)etest
{
return [NSValue value: &etest withObjCType: #encode(enum ETest)];
}
#end
Next we'll add 'enum ETest' support to NSMutableDictionary
#interface NSMutableDictionary (objectForETest)
-(void) setObject:(id)anObject forETest:(enum ETest)key;
-(id) objectForETest:(enum ETest)key;
#end
#implementation NSMutableDictionary (objectForETest)
-(void) setObject:(id)anObject forETest:(enum ETest)key
{
[self setObject: anObject forKey:[NSValue valueWithETest:key]];
}
-(id) objectForETest:(enum ETest)key
{
return [self objectForKey:[NSValue valueWithETest:key]];
}
#end
The original Example can thus be transformed to
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject: #"Bar!" forETest:BAR];
NSLog(#"getting value Bar -> %#", [dict objectForETest: BAR]);
[dict release];
Depending on how much you use your enum to access the dictionary this may ease readability of your code quite a bit.
enums don't conform to NSCopying
This is an understatement; enums do not "conform" to anything as they are not objects; they are primitive C values which are interchangeable with integers. That's the real reason why they can't be used as keys. The keys and values of NSDictionary need to be objects. But since enums are integers, you can just wrap them into NSNumber objects. This is probably the simplest option.
Another option, if the enums are contiguous from 0 up to some number (i.e. you didn't set any values manually), is that you can use an NSArray where the index represents the key enum's value. (Any "missing" entries would have to be filled with NSNull.)
The category approach has its own uses, but the newer boxed expressions (e.g. #(FOO)) should take care of type conversion for you. It works very transparently by explicitly boxing the enum when using it as a key.