How to check NSDictionary for key using Objective-C literals - objective-c

When parsing JSON, I get NSDictionary and go through it one (known) key at a time. When debugging a bug, I started wondering: how should I handle missing key vs value which is zero?
Earlier I would have used something like this to check if key exists or not:
if ([dict objectForKey:#"public"])
newItem.isPublic = [dict objectForKey:#"public"] ? YES : NO;
but now I wrote this using Objective-C object literals. Just started wondering, what does this check actually do:
if (dict[#"public"])
newItem.isPublic = dict[#"public"] ? #YES : #NO;
Should I check against nil or [NSNull null] or is there some easier, obvious, way?

The new literal syntax makes no difference here. The dict[#"public"] code is translated into [dict objectForKeyedSubscript:#"public"], which is documented to be the same as valueForKey:, which in turn calls objectForKey:.
As for the conditional test, writing if ([dict objectForKey:#"foo"]) is the same as if ([dict objectForKey:#"foo"] != nil). In other words, [NSNull null] counts as true here, so you have to check explicitly if you want a different behaviour. Zero can’t be directly stored in a dictionary, it would have to be wrapped in an NSNumber, again being treated as a true value.
See also the Objective-C Literals spec and NSDictionary Class Reference.

Related

Objective-C Bool Literals and Macros

Disclaimer: limited knowledge of Objective-C (passing curiosity).
if (#YES)
vs
if (YES)
What's the difference?
From what I understand, #YES is a bool object literal, and YES is a macro that expands to 1? Is this correct? If so, why use #YES instead of YES or vice versa?
#YES is a shortcut for [NSNumber numberWithBool:YES], so this:
if (#YES)
actually means
if ([NSNumber numberWithBool:YES] != nil)
Note, that it doesn't matter what number (or bool value) that is. You just test that it's a valid object. Even #NO will evaulate to YES if you test it like that, because it's a non-nil NSNumber instance:
if (#NO) NSLog(#"test");
Output:
2013-12-07 21:02:49.828 MyApp[37512:70b] test
Long story short: Don't use #YES or #NO like that, they will not behave as you would expect.
The # symbol is a recent addition to Objective-C, which changes literals into their object representation in the form of an NSNumber instance.
This is especially useful if you need to store literals in arrays and dictionaries, which can only store objects (type id).
The way you use it, there is not need to create an object for the literal, so you should just use the literal directly.
The # symbol has always been used for denoting NSString objects, and only 2-3 years ago this syntax was extended for literals and expressions.
#YES, #1, #1.5...these are all literal syntax that are the equivalent of an NSNumber ie:
[NSNumber numberWithBool:YES], [NSNumber numberWithInt:1], [NSNumber numberWithDouble:1.5].
Using this means you are creating an NSNumber object.
YES is simply the BOOL type, it's a primitive type in Obj-C, not an object like NSNumber. The BOOL type is used in Objective-C to hold true or false values. This is what you would normally use. You would only use #YES in cases where you need to hold the primitives in something that only accepts objects, perhaps if you wanted to hold them in an NSArray for example.

please demystify this null

I am calling a web service in my project, for handling web service result there is a method, which tells what is returned by service. Other methods use this value for further process.
network problem, webserivce problem or any xyz reason, webserivce handling method return (null) value.
Now I have to perform nul checks, two approaches worked for me
if([val isEqualToString:#"(null)"]) ...
if(!val) ...
there are many questions regarding checking null, but not clearing my concepts.
Some says check length if I check the length like val.length it will be 6, because handler method return the "(null)". And !val I am not clear how this works. Some also says check isKindOfClass [NSNull null] ...
Please can any one demystify this null.
Please don't give comparison of nil vs Nil vs Null ...
if(!val) will test if val is null. That should be what you want to use, plain and simple. if(val == nil) is equivalent.
[val isEqualToString:#"null"] will, as the name suggests, test if val is equal to the string "null" which would most certainly make it not null.
[val isKindOfClass:[NSNull class]] has no relevance to you right now, so don't worry about it.
Edit since you asked for an explanation of NSNull:
Sometimes, you need to be able to store the concept of nil rather than nil itself.
Suppose you wanted an array with nil inside it. You want your array to look like [obj1, obj2, nil, obj3].
You can't declare an array like [NSArray arrayWithObjects:obj1, obj2, nil, obj3, nil] because as it goes through the arguments, when it sees nil it'll stop. This will result in the array [obj1, obj2], which is not what you wanted.
So what do you do in a situation like this? You make your array [NSArray arrayWithObjects:obj1, obj2, [NSNull null], obj3]. Now you have an object in your array, but this particular object is one that everyone in the system has agreed to call "null." It's not actually null.
What's really going on here is [NSNull null] is a singleton. When you call [NSNull null], it returns a static instance of NSNull. If you call [NSNull null] again, it'll return that same object, regardless of where you call the method. Because there is exactly one NSNull instance in the whole entire world of your app, it's equivalent to null because there will never be an object at that same address that doesn't have a null meaning.
So, as I said above, it's not relevant to you at all. You only need NSNull if you need a legitimate Objective-C object but you want it to be "null." It's usually safer to use NSNull than simply a string #"(null)" or something like that because it could be possible to overwrite the value of that string, mess up the string comparison, sometime down the line you might need to store the string #"(null)" in the same data structure as you're storing the null object, etc.

Can a block be used as a value for notFoundMarker in iOS?

NSDictionary has the following method signature:
- (NSArray *)objectsForKeys:(NSArray *)keys notFoundMarker:(id)anObject;
My question(s):
Is nil a reasonable default to use for notFoundMarker?
Is it possible to get the key (for which no value was found) back as the notFoundMarker itself? This is useful if the key and value were are different object types, and lets one know what's still missing.
Can a block be used as the value for notFoundMarker, will it actually run? Or will the block's stack-allocated-address simply be returned?
What are some really bad things to try and use as the value for notFoundMarker?
As pointed out in the comments below, you should use [NSNull null]
Probably not without writing your own wrapper method or some sort of category to do this. That said, if you just want to know what key wasn't found, you can simply look at the array of keys that you passed in, as the indexes will match up.
You can certainly use a block. I don't think it will be run. This should be very easy to test though (simply define a block that logs something out to the console and try it).
This really depends on the context and what you're doing to do with the returned array. There's nothing that's inherently bad to put in an array and return, but I'd question the decision to use anything that doesn't match the types of the objects you're expecting to be returned for keys that are found in the NSDictionary.
EDIT:
In fact you could probably achieve what you want for 2. like this:
NSMutableArray *objects = [myDictionary objectsForKeys:myKeys notfoundMarker:[NSNull null]];
for (int ii = 0; ii < [objects count]; ii++ ) {
if ([objects objectAtIndex:ii] == [NSNull null]) {
[objects insertObject:[myKeys objectAtIndex:ii] atIndex:ii];
}
}

What is the value of the string #""?

I'm writing an iPhone application that uses a lot of editable text fields. I've been learning a lot about UITextFields and NSStrings by reading various references online, but there are some details that still elude me. When a user puts in an incorrect value for one of my text fields, I throw up an error message and put the text field back to the way it was before their input. For empty text fields, I've been doing this:
theTextField.text = #"";
Is this the best way to do this? I just came up with the idea myself, I don't know if there are any problems with it (other than the fact that it seems to work just fine so far).
Also, does #"" have the same value as a "nil" string? In other words, if I set a string to #"" and then call this:
if (myString) {...}
will the statement return true or false?
One last thing. When an NSString is initialized using this:
NSString *myString = [[NSString alloc] init];
what is that string's Length value?
The important thing to understand here is that an NSString with no characters in it, such as #"" or [[NSString alloc] init] is still a valid object. All the consequences that Nick has stated follow from that.
In Objective-C, any valid object will be "True" in a boolean context;* nil is the only false object value.
Since these strings are valid objects, they do have a length, but because they contain no characters, the length is 0.
There are no problems with assigning an empty string object #"" to another string pointer, such as the text of your text field. Since the string with no characters is still a valid NSString object, this is exactly the same as assigning a string which does happen to have characters.
*Unlike so-called "scripting" languages like Python or Perl, where an empty string or collection evaluates to boolean false.
Using
theTextField.text = #"";
is absolutely ok. There should be no problems at all.
if (#"")
will evaluate to true. #"" is not the same as nil.
The length of
NSString *myString = [[NSString alloc] init];
is 0.
This is not an answer to the question, but may be the answer to what you're trying to do.
If you're wondering whether you have to write if(str && str.length) to cover both nil and empty strings, you don't. You may use just if(str.length), since, in Objective-C, unknown messages to nil will return nil (so [a.b.c.d.e.f doStuff] will be nil if any of those values in the chain is nil). There is thus scarce need for specific nullity checks, unless what you want is precisely to determine nullity.
Check NSString's + string.

Objective-C for Dummies: How do I get a value out of NSDictionary?

so I'm having the most difficult of time pulling values out of an NSDictionary. Right now I just have a dictionary that is populated from a JSON call and it only contains a key named 'Success' with a value of 0 or 1.
How do I do a conditional on that value to check if its 0 or 1? I've tried a bunch of things, but I'm not getting anywhere. Here's my current code:
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber:1]
I'm getting passing argument 1 of 'isEqualToNumber:' makes pointer from integer without a cast' as a warning, and the app crashes when it hits that line anyway.
And a subquestion, what's the difference between objectForKey and valueForKey? Which one should I use by default?
Anyway, this noob in Objective-C would truly appreciate some help on this. Thanks in advance!
Since dictionaries contain Objective-C objects, an entry containing a number is an NSNumber instance. NSNumber provides a convenience method, -intValue, for extracting its underlying int value:
if ([[jsonDictionary objectForKey:#"Success"] intValue] == 1) { … }
Note that NSNumber has other convenience methods for extracting its underlying value as other C data types.
In most cases, you should use -objectForKey: instead of -valueForKey:. The former is the canonical method to obtain an entry in the dictionary and is declared in NSDictionary. The latter is declared in NSObject and is used in Key-Value Coding contexts, where the key must be a valid KVC key, and there’s additional processing — for instance, if you’re using -valueForKey: in a dictionary with a key that starts with #, that character is stripped from the key and [super valueForKey:key] is called.
The number 1 is not an object pointer. Use an NSNumber instance instead if you want to use a number in an NSDictionary.
[[jsonDictionary objectForKey:#"Success"]
isEqualToNumber:[NSNumber numberWithInteger:1]]
[[jsonDictionary objectForKey:#"Success"] isEqualToNumber: [NSNumber numberWithInt:1]]
Number and Value Programming Topics: Using Numbers
NSNumber: What is the point ?
You can get the value of dictionary in different ways like checking
the value first.
Solution 1: Using simple if statement.
int value = 0;
if ([[jsonDictionary objectForKey:#"Success"]intValue]==1){
value = [[jsonDictionary objectForKey:#"Success"]intValue];
}
Solution 2: Using ternary operator
value = ([[jsonDictionary objectForKey:#"Success"]intValue]==1) ? 1:0;