How to detect if NSString is null? - objective-c

I have a piece of code that detects if a NSString is NULL, nil, etc. However, it crashes. Here is my code:
NSArray *resultstwo = [database executeQuery:#"SELECT * FROM processes WHERE ready='yes' LIMIT 0,1"];
for (NSDictionary *rowtwo in resultstwo) {
NSString *getCaption = [rowtwo valueForKey:#"caption"];
if (getCaption == NULL) {
theCaption = #"Photo uploaded...";
} else if (getCaption == nil) {
theCaption = #"Photo uploaded...";
} else if ([getCaption isEqualToString:#""]) {
theCaption = #"Photo uploaded...";
} else if ([getCaption isEqualToString:#" "]) {
theCaption = #"Photo uploaded...";
}
}
And here's the error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull isEqualToString:]: unrecognized selector sent to instance 0x3eba63d4'
Am I doing something wrong? Do I need to do it a different way?

The NULL value for Objective-C objects (type id) is nil.
While NULL is used for C pointers (type void *).
(In the end both end up holding the same value (0x0). They differ in type however.)
In Objective-C:
nil (all lower-case) is a null
pointer to an Objective-C object.
Nil (capitalized) is a null pointer
to an Objective-C class.
NULL (all caps) is a null pointer to
anything else (C pointers, that is).
[NSNull null] is a singleton for situations where use of nil is not possible (adding/receiving nil to/from NSArrays e.g.)
In Objective-C++:
All of the above, plus:
null (lowercase) or nullptr (C++11 or later) is a null pointer to C++ objects.
So to check against nil you should either compare against nil (or NULL respectively) explicitly:
if (getCaption == nil) ...
or let ObjC / C do it implicitly for you:
if (!getCaption) ...
This works as every expression in C (and with Objective-C being a superset thereof) has an implicit boolean value:
expression != 0x0 => true
expression == 0x0 => false
Now when checking for NSNull this obviously wouldn't work as [NSNull null] returns a pointer to a singleton instance of NSNull, and not nil, and therefore it is not equal to 0x0.
So to check against NSNull one can either use:
if ((NSNull *)getCaption == [NSNull null]) ...
or (preferred, see comments):
if ([getCaption isKindOfClass:[NSNull class]]) ...
Keep in mind that the latter (utilising a message call) will return false if getCaption happens to be nil, which, while formally correct, might not be what you expect/want.
Hence if one (for whatever reason) needed to check against both nil/NULL and NSNull, one would have to combine those two checks:
if (!getCaption || [getCaption isKindOfClass:[NSNull class]]) ...
For help on forming equivalent positive checks see De Morgan's laws and boolean negation.
Edit: NSHipster.com just published a great article on the subtle differences between nil, null, etc.

You should use
if ([myNSString isEqual:[NSNull null]])
This will check if object myNSString is equal to NSNull object.

Preferred Way to check for the NSNULL is
if(!getCaption || [getCaption isKindOfClass:[NSNull class]])

if([getCaption class] == [NSNull class])
...
You can also do
if([getCaption isKindOfClass:[NSNull class]])
...
if you want to be future proof against new subclasses of NSNull.

Just check with this code:
NSString *object;
if(object == nil)
This should work.

Related

null, nill is string type in Objective-C

//result: ok fine
NSString *email = [dataDic objectForKey:#"email"];
if([email isEqualToString:#"null"])
email = nil;
if((![email length]) == 0)
self.emailLbl.text = email;
// result: not fine
NSString *email = [dataDic objectForKey:#"email"];
if((![email length]) == 0 || (email != nil) )
self.emailLbl.text = email;
In dictionary, email property value contained null value. My question is what is the type of nil? -> if comparison email == nil || email == [NSNull null] ?
There are several values that are different from Objective-C perspective here:
nil (aka null in other languages)
[NSNull null] (a special marker value object)
#"" (empty string)
#"null" (just a string with 4 characters)
If you write your dataDic from your app, and you know that your app handles it well, you don't have to check all the cases. Check only the ones you expect. For example, if your app only writes non-empty strings to the dictionary, but sometimes "email" is not there, you only have to check nil, because objectForKey returns nil if the value is not inside the dictionary.
On the other hand if you have obtained dataDic from a 3rd party API, decoded from JSON for example, then you should do the full checking:
[NSNull null] is placed inside the dictionary if JSON has null originally like {"email":null}
If the server API changed you might get some other structure than NSString there (although quite unlikely here).
nil is returned if you don't have the key/value at all.
You can rule out all the 3 checks at once by doing:
NSString *emailStr = nil;
id emailObj = [dataDic objectForKey:#"email"];
if ([emailObj isKindOfClass:[NSString class]]) {
emailStr = emailObj;
}
Note that you might not have to check for an empty string or nil before assigning to UILabel text, because those work fine and just erase the label text:
self.emailLbl.text = #"";
self.emailLbl.text = nil; // another way to erase
You should check whether the key #"email" contain any value or not like (if its a String)
NSString *email=dic[#"email"];
if (email) {
//do anything with email
}
if you want to check your object is Nil Or Null then do
if (!email || email == (id)[NSNull null]) {
//email unavailable
}
Alternately you can call [Obj isKindOfClass:[NSNull class]] on any object.

null check for dictionary object before call to intvalue still leads to intvalue calls on null object

I get an array of dictionaries back from reading json off a web server and use the following to make sure I got a particular key in the first dictionary in the array before getting its int value:
if([jsonObject[0] objectForKey:#"votes"]!= nil)
{
int votes = [[jsonObject[0] objectForKey:#"votes"] intValue];
[[UserObject userUnique] updateVotes:votes];
}
However, my app still occasionally crashes saying I have called intValue on Null. I have also tried structuring the control statement as
if([jsonObject[0] objectForKey:#"votes"])
but this also leads to the same error/app crashing. My syntax seems in line with accepted answers on SO (Check if key exists in NSDictionary is null or not). Any suggestions for what else/how else I should check the existence of key-value pair for applying intvalue?
Thank you for any advice.
There is a difference between nil and null. nil is not an object: it's a special pointer value. null (as retuned by [NSNull null]) is an object: it's needed because it can be stored in containers like NSDictionary.
NSString *votesString = [jsonObject[0] objectForKey:#"votes"];
if (votesString != nil && votesString != [NSNull null])
{
int votes = [votesString intValue];
[[UserObject userUnique] updateVotes:votes];
}
EDIT: An answer to #SunnysideProductions question
The post you mentioned recommends a way of turning null values into nil values by creating a -safeObjectForKey: method. You are not using -safeObjectForKey:, you are using the default -objectForKey: method.
Be consecutive in your code. Don't run with methods. It would be better add more null- and type-checks in particular in working with json. Let's do it:
if (jsonObject && [jsonObject isKindOfClass:[NSArray class]])
{
NSArray *jsonArray=(NSArray *)jsonObject;
if (jsonArray.count>0)
{
id firstObject=jsonArray[0];
if ([firstObject isKindOfClass:[NSDictionary class]])
{
NSDictionary *jsonDict=(NSDictionary *)firstObject;
id votesNumber=jsonDict[#"votes"];
if (votesNumber && [votesNumber isKindOfClass:[NSNumber class]])
{
int votes=[votesNumber intValue];
[[UserObject userUnique] updateVotes:votes];
}
}
}
}
Now the code is more safe. Does it still crash?
When you call objectForKeyin nullable dictionary, app gets crashed so I fixed this from following way.
- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
id object = dictionary;
if (dictionary && (object != [NSNull null])) {
self.name = [dictionary objectForKey:#"name"];
self.age = [dictionary objectForKey:#"age"];
}
return self;
}

Is nil and (NSString *)[NSNull null] equivalent in checking empty NSString object

I have a NSString object,
NSString *aString;
then are the two following versions equivalent?
Version 1 :
if ( (NSString *)[NSNull null] == aString )
{
// Logic handling
}
Version 2 :
if ( nil == aString )
{
// Logic handling
}
Reference Posts
Difference among nil, Nil, and null
How to detect if NSString is null?
Apple's NSNull Class Reference
How do I test if a string is empty in Objective C?
Update - Test Result
My simple test result shows that the above two versions have different behaviors:
When aString is initialized and then assigned with nil:
false for expression in version 1,
true for expression in version 2.
When aString is initialized with the value of #"".
false for expression in version 1,
false for expression in version 2.
So it's clear that the two versions are not equivalent in their behavior.
The test code:
NSString *aString = nil;
NSString *bString = [NSString stringWithFormat:#""];
if ((NSString *)[NSNull null] == aString) {
NSLog(#"a1 - true");
} else {
NSLog(#"a1 - false");
}
if (nil == aString) {
NSLog(#"a2 - true");
} else {
NSLog(#"a2 - false");
}
if ((NSString *)[NSNull null] == bString) {
NSLog(#"b1 - true");
} else {
NSLog(#"b1 - false");
}
if (nil == bString) {
NSLog(#"b2 - true");
} else {
NSLog(#"b2 - false");
}
Console output:
2013-10-31 00:56:48.132 emptyproject[31104:70b] a1 - false
2013-10-31 00:56:48.133 emptyproject[31104:70b] a2 - true
2013-10-31 00:56:48.133 emptyproject[31104:70b] b1 - false
2013-10-31 00:56:48.133 emptyproject[31104:70b] b2 - false
Update - What Do I Mean "Empty string"**
Now I've made it clearer that it's different for a NSString object to be nil and for it to be a valid initialized instance holding an empty string value of #"". What I really need in this post is that how to test if my NSString object is successfully initialized, that is, if aString is nil. I want to know if there is any difference for the above two versions of test code.
[NSNull null] and nil are not equivalent. [NSNull null] is meant to represent the concept of NULL (as in no object) in cases where nil cannot be used, for example in an NSArray (as you can only insert objects in them). [NSNull null] is an object (always the same object), while nil is a pointer to 0.
NSHipster has a nice discussion here. He says:
NSNull is used throughout Foundation and other frameworks to skirt
around the limitations of collections like NSArray and NSDictionary
not being able to contain nil values. You can think of NSNull as
effectively boxing the NULL or nil value so that it can be used in
collections.
If you have:
NSString *aString;
if ( aString == (NSString *)[NSNull null] )
{
// Logic handling
}
then something's wrong, aString should point to an NSString object (or subclass), or nil. But not [NSNull null] which is an object of a different class, you shouldn't cast from one to the other.
EDIT:
Given in the comments you state that you wish to check if the string is empty (as in #""), that is different. See this question. An empty string is an NSString object, it is not nil and it is not [NSNull null].
they are not the same, the NSNull is a valid object (inherited from NSObject) opposite a nil pointer, which points to nothing.
that is how you can check, whether an object is an NSNull object, but you first version is also okay.
id _object = // any kind of NSObject ...
if ([_object isKindOfClass:[NSNull class]]) {
// Logic handling
}
nil means nothing.
[NSNull null] is an object, instance of class NSNull
== means equality
something equals to something else is not the same as something is equal to nothing
[NSNull null] returns the singleton instance of NSNull.
aString == [NSNull null] compares two pointers. As long as aString does not point to the NSNull singleton, they will never be equal.
If you want to match a string to nil:
1. if(aString.length==0)
{
}
2.if(sString isEqualToString:#"")
{
}
3.if(aString!=nil)
{
} else
{
//do your stuff here
}
+(NSString*)replaceNullValuesWithEmptyString:(id)tempObj
{
if (([tempObj isKindOfClass:[NSNull class]])||
(tempObj == nil) ||
(tempObj == (id)[NSNull null])||
[tempObj isEqual:[NSNull null]] ||
[tempObj isEqual:nil]) {
}
else {
if([tempObj respondsToSelector:#selector(isEqualToString:)]) {
if ([tempObj isEqualToString:#"<null>"] ||
[tempObj isEqualToString:#"(null)"]) {
}
else {
if ([tempObj respondsToSelector:#selector(length)]) {
if ([tempObj length]>0) {
NSLog(#"Check Passed.");
return tempObj;
}
}
}
}
}
NSLog(#"Check failed.");
return #"";
}

Compare to null in objective c

I am beginning to find my code littered with:
if([p objectForKey#"somekey"] != [NSNull null]) {
}
Is there shorter (character-wise) comparison for NULL?
Background: I am using the SBJson library to parse a JSON string and there are often null values (by design) for some of the keys.
Nothing built-in, but it would be reasonable and simple to create a function MYIsNull() that would do the comparison you want. Just think through what you want to return in the case that the key is missing.
You may want to go the other way and transform -null into nil. For instance, you could add a category on NSDictionary like this:
- (id)my_nonNullObjectForKey:(NSString *)key {
id value = [self objectForKey:key];
if ([value isEqual:[NSNull null]) {
return nil;
}
return value;
}
I would use
if([[p objectForKey#"somekey"] isEqual:[NSNull null]] || ![p objectForKey#"somekey"]) {
// NSNull or nil
} else {
// Stuff exists...Hurray!
}
It seem to work since [NSNull null] is in fact an "object". Hope it helps!
No, you have to test for NSNull. However, if you're finding your code is being littered by it, you might want to create a #define for it.
Bear in mind also that if p is nil, or if p doesn't have a value for someKey, then [p objectForKey#"somekey"] != [NSNull null] evaluates to YES.
So you probably want something like this:
#define IsTruthy(X) ( X && (X != [NSNull null]) )
Is there shorter (character-wise) comparison for NULL?
[NSNull null] is 13 chars. You can say:
NSNull.null // << 11
(id)kCFNull // << 11
Or make a function:
IsNSNull([p objectForKey#"somekey"]) // << 10 in this case and requires no ==, !=
Or (cringes) use a category:
[p objectForKey#"somekey"].mon_isNSNull // << 13 in this case, but requires no ==, !=
Just be careful how you name that category when dealing with nil receivers.
Since you are using SBJSON, you can easily change its code - you have the source.
I have actually modified SBJSON parser to skip [NSNull null] values. They are not added to the dictionaries and when I call objectForKey:, I never get [NSNull null], I just get nil. Then, in most situation I don't even have to check if the value is nil since calling a method on nil usually gives the result I expect.
If you're just worried about the amount of time your taking to type, consider macros:
#define ISNULL(key) [p objectForKey:key] == [NSNull null]
then
if (!ISNULL(#"somekey")) ...
Pretty sure you can just say
if ([p objectForKey:#"somekey"]) {
}
I don't use NSNull much so I'm not 100% sure but I think it tests as false and any other object tests as true.

Objective C NSPredicate predicateWithBlock removing nil/null values

I am trying to populate an array by taking an existing array and removing nil values from it. The array was populated from a the JSON response of an http call. Sometimes the array has a null value at the end, and the easiest way to remove that value so I wouldn't have to handle it everywhere in my code would be to use NSArray's filteredArrayUsingPredicate: to assign the variable into the instance variable I use throughout my class.
NSArray *respAgencyList = (NSArray*) [JSON valueForKeyPath:#"xml.path.to.data" ];
NSLog(#"data before filter: %#", respAgencyList);
// prints: ( { domain: "foo.com", name:"foobar"}, "<null>" });
if (respAgencyList != nil && respAgencyList.count > 0) {
agencies = [respAgencyList filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSLog(#"Evaluated object is %#", evaluatedObject); //prints <null> for the null value
BOOL ret = evaluatedObject != nil;
return ret;
}]];
}
In the above code the return value is always YES. However, when I put the debugger on and step through it I see:
evaluatedObject = id 0x00000000
Isn't this a null/nil value? What is different about this value compared to nil?
You should also check for NSNull, which can be placed into an NSArray since it is a proper object.
BOOL ret = (evaluatedObject != nil && [evaluatedObject isKindOfClass:[NSNull class]] == NO);
It is impossible for an NSArray to contain a nil element.
Some enumeration methods do hand you nil after the enumeration, as a signal that you've reached the end, but the nil is not in the array — it's just a signal, and you are not expected to do anything serious with it. However, I do not know whether this is one of them.
I suggest that instead of trying to remove nil from the array, which is impossible since nil was never there in the first place, you examine the array directly (log it, look in the debugger, whatever) and assure yourself that what you're trying to do is unnecessary.