Can't validate null string in Objective-C - objective-c

I have the following code:
NSString *content = [[NSUserDefaults standardUserDefaults] stringForKey:#"mykey"];
NSLog(#"string is %#",content);
if ([content stringIsEmpty]){
NSLog(#"empty string");
}else{
NSLog(#"string is not empty");
}
stringIsEmpty is class category on NSString:
- (BOOL ) stringIsEmpty {
if ((NSNull *) self == [NSNull null]) {
return YES;
}
if (self == nil) {
return YES;
} else if ([self length] == 0) {
return YES;
}
return NO;
}
The output is:
string is (null)
string is not empty
How could it be null and not empty at the same time?

What happens is that:
[content stringIsEmpty:YES]
will return false (NO), when content is nil. So your code will take the
NSLog(#"string is not empty");
branch. This would be better:
if (content && [content stringIsEmpty:YES]){
...
A better way of doing this would be reversing the semantics of the method:
if ([content stringIsNotEmpty]) {
this would work finely because when content is nil it would return NO, when it is not nil, it would execute your method.
EDIT:
In Objective-C, sending a message to nil is legal and by definition will evaluate to nil. Google for "objective c sending message to nil".
In another language (C++), your code would crash (actually undefined behaviour, but to make things simple).

I use a small function to test for emptiness. It works on more than just strings:
static inline BOOL isEmpty(id thing) {
return thing == nil
|| ([thing respondsToSelector:#selector(length)]
&& [(NSData *)thing length] == 0)
|| ([thing respondsToSelector:#selector(count)]
&& [(NSArray *)thing count] == 0);
}
I usually import it in my pch file - you can see it along with attribution: https://gist.github.com/325926
As #sergio has already pointed out - when your string is nil you can't send it messages that test it for nil-ness - as sending messages to nil will do nothing for void methods, and return nil where the method returns something.
also
you are calling your method with a parameter
if ([content stringIsEmpty:YES])
but your method declaration doesn't take one:
- (BOOL ) stringIsEmpty {
What's that all about?

You have to check for the 'content == nil' case outside of the method.
If you want to be able to call just one method, change the method to something that tests for a positive, such as "stringHasContent", returning YES if self.length > 0.

Related

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 #"";
}

how to check for null object returned in xcode

I have a NULL object returned from a JSON query string and I don't know how to check for it in an If statement. My syntax is below but I still don't seem to be able to trap for the NULL class (i.e., if nothing is returned then no text variable can be set therefore it MUST be a NULL class?), anyway, I need to check that the #"BillingStreet" has something in it and if not to avoid processing it (else the app crashes as it tries to set nothing to the text value of one of the fields in the VC):
- (void) tableView: (UITableView *)itemTableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
NSDictionary *obj = [self.dataCustomerDetailRows objectAtIndex:indexPath.row];
NSString *text = [obj objectForKey:#"BillingStreet"] == nil ? #"0" : [obj objectForKey:#"BillingStreet"];
NSLog(#"%#",text);
if (text.class == NULL){
} else {
NSLog(#"no street");
self.labelCustomerAddress.text = [obj objectForKey:#"BillingStreet"];
}
}
A JSON "null" value is converted to [NSNull null], which you can check
for with
if (text == [NSNull null]) ...
because it is a singleton.
Alternatively, you can check if the object contains the expected type, i.e. a string:
NSString *text = [obj objectForKey:#"BillingStreet"];
if ([text isKindOfClass:[NSString class]]) {
self.labelCustomerAddress.text = text;
} else {
self.labelCustomerAddress.text = #"no street";
}
This is more robust in the case that a server sends bad data, e.g. a number or an array instead of a string.
text == nil ? #"0" : text
or
text ? text : #"0"
But if you get it from JSON then you may get instance of NSNull class. In this case you should check
text && ![text isKindOfClass:[NSNull class]] ? text : #"0"

Bool function for xcode

I am practicing some bool functions and I seem to be stuck any help will be appreciated. I must be making some little mistake.
-(BOOL) checkForWin
{
if ([[dictionary valueForKey:[cowsShuffled objectAtIndex:cowsCard]] intValue] == 2{
return YES;
}
}
-(void) moo
{
if (checkForWin == YES) {
NSLog (#"foo");
}
}
You need to call the method (not function), and you don't need to compare to YES. The if statement does that implicitly:
if ([self checkForWin]) …
Also note that checkForWin has a problem: it doesn't return anything if the if statement fails. It should be simply:
- (BOOL)checkForWin{
return [[dictionary valueForKey:[cowsShuffled objectAtIndex:cowsCard]] intValue] == 2;
}
Footnote: Strictly speaking, if (x) … isn't exactly the same as if (x == YES) …. It's actually closer to if (x != NO) …, but of course that's the same thing for most intents and purposes (and those for which it isn't are largely pathological).
Your method call is wrong. You call a method like this: [object method].
In your case [self checkForWin].

NSLog gives runtime error

Ok, so I know I can just leave out the NSLog but why does it give me a "EXC_BAD_ACCESS" error?
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex2
{
if(buttonIndex2 == 0 && waitForAction == NO)
{
waitForAction = YES;
[self showAbortAlert];
NSLog(#"%#",buttonIndex2); //This one does not crash the app
} else if (buttonIndex2 == 1 && waitForAction == NO)
{
waitForAction = YES;
[self addObject];
NSLog(#"%#",buttonIndex2); //This one crashes the app
} //else if
}
see the method signature again
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex2
buttonIndex2 is of type NSInteger. If you do %# in NSLog your code is calling the description method on the object. But buttonIndex2 is not an object.
use NSLog(#"%d",buttonIndex2);
The first one (with buttonIndex == 0) does not crash the app because you are calling description on an object at memory address 0, which is basically the same as [nil description] and this is perfectly legal in Objective-C.
buttonIndex2 is an integer use :
NSLog(#"%d",buttonIndex2);
And go read some good book on C.
Use %d as it is an integer, %# is for strings...
NSLog(#"%d", buttonIndex);

How to check if an NSDictionary or NSMutableDictionary contains a key?

I need to check if an dict has a key or not. How?
objectForKey will return nil if a key doesn't exist.
if ([[dictionary allKeys] containsObject:key]) {
// contains key
}
or
if ([dictionary objectForKey:key]) {
// contains object
}
More recent versions of Objective-C and Clang have a modern syntax for this:
if (myDictionary[myKey]) {
}
You do not have to check for equality with nil, because only non-nil Objective-C objects can be stored in dictionaries(or arrays). And all Objective-C objects are truthy values. Even #NO, #0, and [NSNull null] evaluate as true.
Edit: Swift is now a thing.
For Swift you would try something like the following
if let value = myDictionary[myKey] {
}
This syntax will only execute the if block if myKey is in the dict and if it is then the value is stored in the value variable. Note that this works for even falsey values like 0.
if ([mydict objectForKey:#"mykey"]) {
// key exists.
}
else
{
// ...
}
When using JSON dictionaries:
#define isNull(value) value == nil || [value isKindOfClass:[NSNull class]]
if( isNull( dict[#"my_key"] ) )
{
// do stuff
}
I like Fernandes' answer even though you ask for the obj twice.
This should also do (more or less the same as Martin's A).
id obj;
if ((obj=[dict objectForKey:#"blah"])) {
// use obj
} else {
// Do something else like creating the obj and add the kv pair to the dict
}
Martin's and this answer both work on iPad2 iOS 5.0.1 9A405
One very nasty gotcha which just wasted a bit of my time debugging - you may find yourself prompted by auto-complete to try using doesContain which seems to work.
Except, doesContain uses an id comparison instead of the hash comparison used by objectForKey so if you have a dictionary with string keys it will return NO to a doesContain.
NSMutableDictionary* keysByName = [[NSMutableDictionary alloc] init];
keysByName[#"fred"] = #1;
NSString* test = #"fred";
if ([keysByName objectForKey:test] != nil)
NSLog(#"\nit works for key lookups"); // OK
else
NSLog(#"\nsod it");
if (keysByName[test] != nil)
NSLog(#"\nit works for key lookups using indexed syntax"); // OK
else
NSLog(#"\nsod it");
if ([keysByName doesContain:#"fred"])
NSLog(#"\n doesContain works literally");
else
NSLog(#"\nsod it"); // this one fails because of id comparison used by doesContain
Using Swift, it would be:
if myDic[KEY] != nil {
// key exists
}
Yes. This kind of errors are very common and lead to app crash. So I use to add NSDictionary in each project as below:
//.h file code :
#interface NSDictionary (AppDictionary)
- (id)objectForKeyNotNull : (id)key;
#end
//.m file code is as below
#import "NSDictionary+WKDictionary.h"
#implementation NSDictionary (WKDictionary)
- (id)objectForKeyNotNull:(id)key {
id object = [self objectForKey:key];
if (object == [NSNull null])
return nil;
return object;
}
#end
In code you can use as below:
NSStrting *testString = [dict objectForKeyNotNull:#"blah"];
For checking existence of key in NSDictionary:
if([dictionary objectForKey:#"Replace your key here"] != nil)
NSLog(#"Key Exists");
else
NSLog(#"Key not Exists");
Because nil cannot be stored in Foundation data structures NSNull is sometimes to represent a nil. Because NSNull is a singleton object you can check to see if NSNull is the value stored in dictionary by using direct pointer comparison:
if ((NSNull *)[user objectForKey:#"myKey"] == [NSNull null]) { }
Solution for swift 4.2
So, if you just want to answer the question whether the dictionary contains the key, ask:
let keyExists = dict[key] != nil
If you want the value and you know the dictionary contains the key, say:
let val = dict[key]!
But if, as usually happens, you don't know it contains the key - you want to fetch it and use it, but only if it exists - then use something like if let:
if let val = dict[key] {
// now val is not nil and the Optional has been unwrapped, so use it
}
I'd suggest you store the result of the lookup in a temp variable, test if the temp variable is nil and then use it. That way you don't look the same object up twice:
id obj = [dict objectForKey:#"blah"];
if (obj) {
// use obj
} else {
// Do something else
}
if ([MyDictionary objectForKey:MyKey]) {
// "Key Exist"
}
As Adirael suggested objectForKey to check key existance but 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;
}
if ( [dictionary[#"data"][#"action"] isKindOfClass:NSNull.class ] ){
//do something if doesn't exist
}
This is for nested dictionary structure