Objective-C && doesn't short circuit for !var - objective-c

Try running this:
UIView *testView = nil;
NSLog(#"Take 1");
NSString *message = #"view doesn't exist";
if (!testView && !testView.subviews) {
message = #"this message should never appear" ;
}
NSLog(message);
NSLog(#"Take 2");
message = #"view doesn't exist";
if (testView != nil && testView.subviews != nil) {
message = #"this message should never appear" ;
}
NSLog(message);
NSLog(#"Take 3");
message = #"view doesn't exist";
if (!testView) {
if (!testView.subviews) {
message = #"this message should never appear" ;
}
}
NSLog(message);
NSLog(#"Take 4");
message = #"view doesn't exist";
if (testView != nil) {
if (testView.subviews != nil) {
message = #"this message should never appear" ;
}
}
NSLog(message);
Output I get is:
Take 1
this message should never appear
Take 2
view doesn't exist
Take 3
this message should never appear
Take 4
view doesn't exist
Why doesn't Obj-C short circuit for !testView (in Take 1)?
Why does it go into !testView when testView is clearly nil in Take 3?
Should I not be testing the function of a nil object (e.g. when I test for subviews)?

The output you see is correct and the short-circuit behavior is working correctly too.
When boolean expressions are evaluated the result is considered to be true if the expression is not 0 or false if it is 0. So everywhere where you have if (something) you can read this as if (something != 0). The ! operator is the negation, so if you expand it you get the following for your first case: !(testView != 0) && !(testView.subviews != 0). The double negation can be removed and you get (testView == 0) && (testView.subviews == 0) which obviously is true (nil is 0 too).
There the short-circuiting is also correctly applied, you just can't see it. To prove that you could use a little wrapper function for your tests:
id testFunc( id value ) {
NSLog(#"testFunc: %#", value );
return value;
}
And use that in your tests: if (!testFunc(testView) && !testFunc(testView.subviews))
To make it short, your assumptions about the boolean not operator ! are wrong. It goes into if (!testView) because testView is nil.

There's nothing strange going on here. I rewrote your code, substituting testView for nil and testView.subviews for nil (messages to nil return nil).
NSLog(#"Take 1");
NSString *message = #"if clause was false";
if (!nil && !nil) { // if(true && true)
message = #"if clause was true" ;
}
NSLog(message); // outputs "if clause was true"
NSLog(#"Take 2");
message = #"if clause was false";
if (nil != nil && nil != nil) { // if (false && false)
message = #"if clause was true" ;
}
NSLog(message); // outputs "if clause was false"
NSLog(#"Take 3");
message = #"if clause was false";
if (!nil) { // if (true)
if (!nil) { // if (true)
message = #"if clause was true" ;
}
}
NSLog(message); // outputs "if clause was true"
NSLog(#"Take 4");
message = #"if clause was false";
if (nil != nil) { // if (false)
if (nil != nil) { // if (false)
message = #"if clause was true" ;
}
}
NSLog(message); // outputs "if clause was false"
If you want to know if the view exists, you can use something like:
if (testView) {
NSLog(#"View exists a.k.a. testView != nil");
} else {
NSLog(#"View doesn't exist a.k.a. testView == nil");
}
I'd recommend just using if (testView) and if (!testView) to check for the existence of objects. This is the standard in the Objective-C community and reads more clearly.

Related

Is there a way to hide an if statement in objective-c?

I am writing a thread that should be stopped if a button is pressed.
This means that for every action that I do, I also want to check if the stop flag is set so I can stop properly.
This is a piece of the code which should make it clearer:
while([self checkForDrop] != 0 && STOP == FALSE){
sleep(0.4);
} if (STOP == TRUE){
return 99;
}
ret = [self doSomething]
if(ret == 0){
[self updateStatus]
} else {
printf("%d", ret) // Print error
}
ret = [self doSomethingElse]
if(ret == 0){
[self updateStatus]
} else {
printf("%d", ret) // Print error
}
... and so on ...
Every time I do something, I want to also check if a condition is met (STOP == TRUE), if it is, the program should return 99 and stop.
I could write an if statement for every line, but that would make the code unreadable and ugly. I would guess that I am not the only one that stumbled upon this, so there should be a better way.

How to change text field text using if/else if string is NULL or 0?

Below is my code:
if(PREREQUISITEstring == (NSString *)[NSNull null] || [PREREQUISITEstring isEqual: #"0"]) {
PREREQUISITEfield.text = #"2";
} else {
PREREQUISITEfield.text = PREREQUISITEstring;
}
I am 100% sure that PREREQUISITEstring is "0" and it shows as "0" when I'm debugging but when it comes to runtime, that string doesn't change to 2, it stays as 0.
edit: I got the code to work by using this instead
if(PREREQUISITEstring == (NSString *)[NSNull null] || [PREREQUISITEstring isEqual: #"0"]) {
PREREQUISITEstring = #"2";
} else {
PREREQUISITEfield.text = PREREQUISITEstring;
}
in the if statement, i changed PREREQUISITEfield.text -> PREREQUISITEstring
Stop in the debugger at the start of the if statement and check both conditions in the debugger. Obviously, both are false, but there's not enough context provided to tell you why.
Also, are you sure you mean to compare to [NSnull null] and not nil?
if(PREREQUISITEstring == (NSString *)[NSNull null] || [PREREQUISITEstring isEqual: #"0"]) {
PREREQUISITEstring = #"2";
} else {
PREREQUISITEfield.text = PREREQUISITEstring;
}
this is the code i used, replace
PREREQUISITEfield.text
with
PREREQUISITEstring

Checking to see if ObjectForKey exists [duplicate]

Most of the examples I found on the net write this:
if(x != nil)
// ...
Is there any problems with this?
if(x)
// ...
I tried both in a simple program and couldn't found any difference.
In Objective-C, nil is defined as a value called __DARWIN_NULL, which essentially evaluates to 0 or false in if-statements. Therefore, writing
if (x == nil) is the same as writing if (!x) and writing if (x != nil) is equal to if (x) (since comparing to false creates a negation, and comparing to true keeps the condition the same).
You can write your code either way, and it really depends on which you think is more readable. I find if (x) to make more sense, but it depends on your style.
It's like comparing if (someCondition == true) versus if (someCondition).
It all depends on you, and who's going to be reading the code.
Edit: As Yuji correctly mentions, since Objective-C is a superset of C, any condition that evaluates to a value other than 0 is considered to be true, and therefore, if someCondition in the example above were to evaluate to an integer value of, say, -1, comparing it to true would result in false, and the if-statement would not be evaluated. Something to be aware of.
Both
if (x != nil)
and
if ( x )
are equivalent, so pick the variant that in your opinion makes your code more readable for you (and others who will read and support your code)
Both are the same and this is a style question and it boils down to whether you prefer:
if (something) { ... }
versus
if (something != nothing) { ... }
I have always found #1 more clear but #2 is used extensively in documentation and hence the field so it is better to both know both forms and adapt to what a project uses and be stylistically consistent.
The best and safe way to check nil is
Make a common method, and add all these null :
+ (NSString *)trimWhiteSpaceAndNewLine:(NSString *)string {
NSString *stringSource = [NSString stringWithFormat:#"%#",string];
if ([stringSource isEqualToString:#"(null)"]) {
stringSource = #"";
return stringSource;
}
if ([stringSource isEqualToString:#"<null>"]) {
stringSource = #"";
return stringSource;
}
if ([stringSource isEqualToString:#"<nil>"]) {
stringSource = #"";
return stringSource;
}
if ([stringSource isKindOfClass:[NSNull class]]) {
stringSource = #"";
return stringSource;
}
if ([stringSource isEqualToString:#""]) {
stringSource = #"";
return stringSource;
}
if (stringSource == nil) {
stringSource = #"";
return stringSource;
}
NSString *stringFinal = [stringSource stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
return stringFinal;
}
And check
NSString *strUuid = [Common trimWhiteSpaceAndNewLine:[dict valueForKeyPath:#"detail.uuid"]];
if (![strUuid isEqualToString:#""]) {
// do your stuff
}

conditional operator without second attribute

I have realized I'm not fully understand conditional operator when the second value is missing. Can anyone explain me (and paste equivalent with if-else statement) the following code:
if (self.root && [data isKindOfClass:[NSDictionary class]]) {
data = [data objectForKey:self.root] ? : data;
}
The ternary operator with no first element, e.g.
variable ?: anotherVariable
means the same as
(variable != nil) ? variable : anotherVariable
Here is some nice explanation about ternary operator in Objective-C.
u can do like this
to avoid confusion we take your sample code as
if (self.root && [data isKindOfClass:[NSDictionary class]]) {
myData = [data objectForKey:self.root] ? : data;
}
u can replace it with
if (self.root && [data isKindOfClass:[NSDictionary class]])
{
if([data objectForKey:self.root])
{
//if the condition is true (if data is non-nil)
myData = [data objectForKey:self.root]
}
else
{
//goes for false (if data is nil)
myData = data
}
}
for your case it goes like below
if (self.root && [data isKindOfClass:[NSDictionary class]]) {
myData = [data objectForKey:self.root] ? : data; //if the object found for key "self.root" then myData will hav the object for key "self.root" otherwise it "myData" hav "data"
}
lets take a simple example
//for example
BOOL aBoolValue = NO;
int num = (aBoolValue == YES) ? 100 : 50; //if aBoolValue is YES then num has 100 otherwise 50
//in the above example num contains 50

Can't validate null string in 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.