Looping over BOOLs in Objective-C - ios

Is it safe to loop over Objective-C BOOLs like so:
for (BOOL flagA = NO; flagA <= YES; flagA++)
for (BOOL flagB = NO; flagB <= flagA; flagB++)
// ...
I'd like to use this to cycle through all relevant permutations of flags in a XCTestCase.
But it appears as if YES++ is still YES at least on some platforms (hence leading to an infinite loop e.g. on iPhone 6 Plus simulator) whereas I would have expected that BOOL would just be treated as int (and hence YES++ becoming 2).
Do I have to loop over ints (my best guess) instead, or can the use of BOOL be salvaged in some convenient way?

You are all missing the point here. Drux is asking why can't he increment over BOOL, while it should be a char (8 bit value), which is perfectly incrementable.
The Answer is very easy. BOOL is sometimes a char and sometimes a bool depending on the target. From objc.h file:
#if !defined(OBJC_HIDE_64) && TARGET_OS_IPHONE && __LP64__
typedef bool BOOL;
#else
typedef signed char BOOL;
If you iterate over a bool you will get value of 1 maximum.
EDIT:
Can you please add a reference to where the semantics of ++ for bool are specified? - Drux
Even though that bool has to be 8 bits minimum, it can't have any other value than 0 or 1. Why ? Because bool a = 3 (bool equal operator) converts 3 into a bool value, which is true which is 1.
So bool a = true; a++ is the same as bool a = 2; which makes a have a value of 1

The only way I see would be adding a break in your loop to escape the infinite loop.
Another possibilities is to use simple integer and stop the for loop when counter == 2
for (BOOL flagA = NO; YES; flagA++) {
for (BOOL flagB = NO; YES; flagB++) {
// Do something
if (flagB)
break;
}
if (flagA)
break;
}

I think #Sulthan means something like this (made overly explicit on purpose):
for(int indexA = 0; indexA <= 1; indexA++){
for(int indexB = 0; indexB <= indexA; indexB++){
BOOL flagA = (indexA == 1) ? YES : NO;
BOOL flagB = (indexB == 1) ? YES : NO;
// Use your flags (booleans) here...
}
}
(Of course, you can use just the ints in place of booleans in Objective-C, if you want to avoid using too many redundant variables).
ADDENDUM: I actually performed a "Jump to definition" in Xcode (OSX project), and the part looks like this:
#if __has_feature(objc_bool)
#define YES __objc_yes
#define NO __objc_no
#else
#define YES ((BOOL)1)
#define NO ((BOOL)0)
#endif
(usr/include/objc/objc.h)
Can't "Jump to Definition" on __objc_yes (gives "Symbol Not Found")

If you're set on operating over BOOLs, then instead of:
for (BOOL flagA = NO; flagA <= YES; flagA++)
for (BOOL flagB = NO; flagB <= flagA; flagB++)
// ...
You should really be doing something this (though it is not what you want):
for (BOOL flagA = NO; flagA != YES; flagA = !flagA)
for (BOOL flagB = NO; flagB != flagA; flagB = !flagB)
// This is the only safe way to 'iterate' BOOLs
The behaviour, (BOOL)++ is not well-defined* as a BOOL can only be YES or NO. What you really should be doing is casting your BOOL to an int, and iterating over that, or refactoring your loop entirely to use int types.
The problem with casting your BOOL values to ints is, as you have pointed out, BOOL is typedef'd to something with only 8 bits of information*, therefore it only makes sense to have 255 iterations. In fact in more recent times, BOOL is not cast-able at all because it is defined as a compiler intrinsic (objc_bool, which can have values __objc_yes and __objc_no). __objc_no++ has no meaning.
TL;DR My (strong) suggestion would be to refactor your code so you are iterating over integers, and inspecting BOOLs within each iteration. Whether you cast your BOOL values, or refactor your loop is up to you, but iterating over BOOL values in the way you have indicated is both unsafe and (now, because of that) unsupported.
* In past years, the implementation details of BOOL were obvious (namely a cast to an unsigned char). With the advent of compiler intrinsics, the details are hidden (though they are likely the same). The reason they are now hidden is because you're really not supposed to rely on them, and the easiest way to stop people relying on them is to hide them from the compiler altogether.

Related

iOS : NSInteger and NSUInteger comparison

Surprise!
I've a variable like this,
NSInteger index = 0;
I'm comparing it with one of subviews count (which returns NSUInteger) like this,
if((index-1) <= [[currentmonth subviews] count])
{
NSLog(#"true");
}
else
{
NSLog(#"false");
}
This always giving false.
but If I'll do like this,
if ((index-1) <= 42) {
NSLog(#"true");
} else {
NSLog(#"false");
}
This always giving true.
I feel that, this because we can't compare NSInteger with NSUInteger correct?
I caught this issue, when I have a working solution based on this logic. But its not true at all.
I've found this, NSUInteger vs NSInteger, int vs unsigned, and similar cases
This answer gives the good explanations on this!
You should also be aware of integer conversion rules when dealing with NSUInteger vs. NSInteger:
The following fragment for example returns 0 (false) although you'd expect it to print 1 (true):
NSInteger si = -1;
NSUInteger ui = 1;
printf("%d\n", si < ui);
The reason is that the [si] variable is being implicitly converted to an unsigned int!
See CERT's Secure Coding site for an in-depth discussion around these 'issues' and how to solve them.

How to understand BOOL *

Like in this NSArray instance method enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop),as I know BOOL is a primitive type,How can we declare it as a pointer type? why it's not BOOL stop here?
You can wrap other non-object types (such as a pointer or a struct) in an NSValue.
Assuming you really mean a BOOL* (pointer):
NSValue *boolValue = [NSValue value:pointerToBool withObjCType:#encode(BOOL*)];
BOOL *b = [boolValue pointerValue];
Because you're defining stop as a pointer to a BOOL, not as a BOOL itself.
It lets you do things like pass-by-reference emulation in C and Obj-C:
void changeMe (BOOL *pointerToBool) {
*pointerToBool = ! (*pointerToBool); // Dereference the address
// to get at variable.
}
:
BOOL myBool = YES;
changeMe (&myBool); // Pass its address.
BOOL * is pointer of BOOL var. If use it (a pointer) in function argument you can change value of this pointer if you want. Use pointer in argument as a void function, but you can return one or more value in argument
It will help you knowing that BOOL is a signed char, so it can be treated as a it. It's declared in objc.h: http://www.opensource.apple.com/source/objc4/objc4-371.1/runtime/objc.h
The declaration:
typedef signed char BOOL;
#define YES (BOOL)1
#define NO (BOOL)0
BOOL* is pretty much the same as char* , and I'm sure you are more used to see the last.

BOOL property from a calculation returns NSNumber with incorect value using valueForKey:

I have a simple object which has one NSNumber which is used to store some flags.
I have a conienience getter method which in fact does:
[self.flags integerValue] & SomeConstantFlag
for a property#property (readonly, nonatomic, assign) BOOL someConstantFlag
and this works fine when accesing the underlying bool value like
model.someConstantFlag
but when I try to
id value = [model valueForKey:#"someConstantFlag"];
Then it returns a bad boolean representation e.g. NSNumber with value 2, 4 etc.
Why is this happening when the declaration of the property is BOOL? Is there a "Pretty" way to overcome this issue?
Wrapping on the other hand works ok:
BOOL someBool = 42;
NSNumber* numberVal = #(someBool);
//The underlying is an __NSCFBoolean with the proper 0/1 val!
valueForKey always returns an Objective-C object, even if the property has scalar type.
From the documentation (emphasis mine):
The default implementations of valueForKey: and setValue:forKey:
provide support for automatic object wrapping of the non-object data
types, both scalars and structs.
Once valueForKey: has determined the specific accessor method or
instance variable that is used to supply the value for the specified
key, it examines the return type or the data type. If the value to be
returned is not an object, an NSNumber or NSValue object is created
for that value and returned in its place.
The return value of your method is BOOL, which is defined as
typedef signed char BOOL;
on OS X and on the 32-bit iOS platform. So what valueForKey returns is a NSNumber
containing the result of
signed char val = [self.flags integerValue] & SomeConstantFlag;
and that can be in the range -128 .. 127.
To ensure that you get only YES or NO (aka 1 or 0) write your custom getter as:
-(BOOL)someConstantFlag
{
return ([self.flags integerValue] & SomeConstantFlag) != 0;
}
Remark: On the 64-bit iOS platform (but not on 64-bit OS X), BOOL is defined as the C99 _Bool, which is a "proper" boolean type and can take only the value 0 or 1.
NSNumber *value = #([model objectForKey:#"someConstantFlag"]);
BOOL boolVal = [value boolValue];
I think you should consider the following problems. Firstly, integerValue returns NSInteger which means if you support 64Bit architecture it will return int_64 not int_32, what is more in your code here
[self.flags integerValue] & SomeConstantFlag
this does the following if flags is 00010 and somConstantFlags is 00001 the & of those will do something you probably does not expect because you will get value of 00000 which equals 0 or if they are 00011 and 00110 you will get 00010 which equals 2. So that is why when you call valueForKey you get 2 or 4 or something else depending on your flags :)
What is more in objective-C everything different then 0 is YES.
Try reconsidering your bit logic :). See The following example
enum
{
kWhite = 0,
kBlue = 1 << 0,
kRed = 1 << 1,
kYellow = 1 << 2,
kBrown = 1 << 3,
};
typedef char ColorType;
and in your setter check the following
ColorType pinkColor = kWhite | kRed;
if (pinkColor & (kWhite | kBlue | kRed | kYellow)) {
// any of the flags has been set
}
The flags kWhite, kBlue, kRed and kYellow have been set.
However, kBrown has not been set.

Checking that String has a valid number

I'm a programming newbie and I'm currently writing a conversion calc program in objective c and I'm really struggling.
I have a string representing a unsigned long long value. I need a way either when attempting to add another character to check that the new character would not go above LONG_LONG_MAX before adding it. Or deleting the last character if the value is/would be above LONG_LONG_MAX
the only possible way I could think to even try this is:
- (BOOL) isNumberStringValid:(NSString *)stringValue {
unsigned long long uVal = strtoull(stringValue.UTF8String, NULL, 0);
if (uVal <= ULLONG_MAX) return TRUE;
else return FALSE;
}
I know this doesn't work because uVal would always be <= ULLONG_MAX but I can't think of any other ways to possibly check. Can anyone help me find a way to accomplish this???
You can use the fact that strtoull() sets the value of errno to ERANGE if the given
string was out of range:
- (BOOL) isNumberStringValid:(NSString *)stringValue {
errno = 0;
unsigned long long uVal = strtoull(stringValue.UTF8String, NULL, 0);
return (errno == 0);
}
Some test (ULLONG_MAX = 264-1 = 18446744073709551615):
1234 : TRUE
18446744073709551615 : TRUE
18446744073709551616 : FALSE
1844674407370955161678 : FALSE
You can use NSNumberFormatter. Unfortunately NSNumberFormatter stores the 'maximum' value as a float, so there are some problems around the boundary of LONG_LONG_MAX. To deal with that this code checks for nil or a long long value that is negative (which means that it overflowed)
-(BOOL) isNumberStringValid:(NSString *)stringValue
{
[NSNumberFormatter setDefaultFormatterBehavior:NSNumberFormatterBehavior10_4];
NSNumberFormatter *f=[[NSNumberFormatter alloc]init];
NSNumber *max=[NSNumber numberWithLongLong:LONG_LONG_MAX];
[f setMaximum:max];
BOOL valid=NO;
NSNumber *num=[f numberFromString:stringValue];
if (num != nil) // A nil value means that input was > LONG_LONG_MAX
{
long long x=[num unsignedLongLongValue]; // A negative value here means that the input was > LONG_LONG_MAX
if (x>0)
{
valid=YES;
}
}
return valid;
}
the if statement you have is checking if uVal is less than or equal to LONG_LONG_MAX
unsigned long long uVal = (unsigned)stringValue.longLongValue;
if (uVal >= LONG_LONG_MAX) {
return YES;
}
else {
return NO;
}
I ran this and it works fine.

creating default vars (objects) in objective c

dumb question: lets say I'm assigning a var in a conditional statement. I don't know if the condition will be satisfied and i still want the var to be defined.. whats the correct way of writing this
example:
NSDecimalNumber *number = [[NSDecimalNumber alloc]init]; // this is pointless right?
if(x == z){
number = [whatevernum1 decimalNumberByMultiplyingBy: whatevernum2];
} else {
number = [whatevernum2 decimalNumberByDividingBy: whatevernum3];
}
// do something with number variable.
There is no need to initialize number since it will be set. Just do this:
NSDecimalNumber *number;
if(x == z){
number = [whatevernum1 decimalNumberByMultiplying: whatevernum2];
} else {
number = [whatevernum2 decimalNumberByDividing: whatevernum3];
}
// do something with number variable.
In your case number will be assigned a value one way or another. But you might have a situation like this:
if (someCondition) {
// set number to value A
} else if (anotherCondition) {
// set number to value B
}
Here, it is possible that neither condition is met. In this case you need to deal with this properly by initializing number to nil.
NSDecimalNumber *number = nil;
if (someCondition) {
// set number to value A
} else if (anotherCondition) {
// set number to value B
}
if (number) {
// process result
}
You need to declare the variable but not assign it, like this:
NSDecimalNumber *number;
if(x == z){
number = [whatevernum1 decimalNumberByMultiplying: whatevernum2];
} else {
number = [whatevernum2 decimalNumberByDividing: whatevernum3];
}
This tells the compiler that you want to use a variable named number, but don't have a value for it yet. In some cases, you may find it convenient to initialise the variable to nil rather than leaving it as a null pointer.
Normally, as others have pointed out, you would either not initialise (if you can guarantee that you will set a value, eg through an if/else pair), or you would initialise to nil.
In this simple case, a ternary statement would make your code much clearer:
NSDecimalNumber *number = x == z ? [whatevernum1 decimalNumberByMultiplyingBy:whatevernum2] : [whatevernum2 decimalNumberByDividingBy:whatevernum3];

Resources