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

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.

Related

Objective c set value to a property if not null [duplicate]

I have this code
if ([args valueForKey:#"showSetupScreen"]) {
BOOL showSetupScreen = [args valueForKey:#"showSetupScreen"];
NSLog(showSetupScreen ? #"YES" : #"NO");
// meetingConfig.showSetupScreen = showSetupScreen;
}
Where args is NSMutableDictionary.
args value in my dictionary is NO but when I set to BOOL showSetupScreen = [args valueForKey:#"showSetupScreen"]; it changes into YES
Can someone help me in comprehending why this could be happening.
Attached Screenshot for your reference
A NSDictionary (or NSMutableDictionary) cannot directly contain a primitive C type, such as BOOL. Primitive numeric types (including Boolean) in NSDictionary are wrapped in NSNumber objects. See Numbers Are Represented by Instances of the NSNumber Class and Most Collections Are Objects.
Thus, use NSNumber method boolValue to extract the Boolean from the NSNumber, e.g.,
BOOL showSetupScreen = [[args valueForKey:#"showSetupScreen"] boolValue];
Or, more simply:
BOOL showSetupScreen = [args[#"showSetupScreen"] boolValue];
E.g., examples with primitive C types, including BOOL, NSInteger, and double:
NSDictionary *args = #{
#"foo": #NO,
#"bar": #YES,
#"baz": #42,
#"qux": #3.14
};
BOOL foo = [args[#"foo"] boolValue]; // NO/false
BOOL bar = [args[#"bar"] boolValue]; // YES/true
NSInteger baz = [args[#"baz"] integerValue]; // 42
double qux = [args[#"qux"] doubleValue]; // 3.14
For what it's worth, if you expand the values contained within args, that will show you the internal types for those values, and you will see that that the value associated with showSetupScreen (or foo in my example), is not a BOOL, but rather a pointer to a __NSCFBoolean/NSNumber:
[args valueForKey:#"showSetupScreen"] statement returns pointer (address in memory) and it has two options: some address (non zero value) and NULL (zero). For C programming language true is any non zero value (any address in memory in our case). And for this reason you get true in if operator and in showSetupScreen variable. But it only tells you that there is some object in the dictionary for the specified key, but not the value of this key (the value wrapped in this object). To get this value (BOOL in our case), you must call the boolValue.

Objective-C passing object to function is always by reference or or by value?

In objective-c I am passing NSMutableDictionary to function and modifying it inside function it returns modified mutable dictionary :
NSMutableDictionary *obj2 = [[NSMutableDictionary alloc]initWithObjectsAndKeys:#"hello",#"fname",nil];
[self callerDictionary:obj2];
NSLog(#"%#",obj2[#"fname"]);//printing "Hi"
-(void)callerDictionary:(NSMutableDictionary*)obj
{
obj[#"fname"] = #"Hi";
}
Technically, Objective C always passes parameter by value, as does C, but practically when you pass an object you need to pass a pointer. While this pointer is passed by value, the semantics of Objective-C give the same effect as if you had passed an object reference; if you modify the objected that is pointed to by the pointer then you are modifying the same object instance that is pointed to in the calling context. The common terminology used in Objective C programming is "object reference" even though it is really a pointer value.
You can see from the * in the method signature that it is a pointer (or object reference in the common usage). If you are passing an intrinsic type, such as an int then it is passed by value unless you explicitly declare the method as requiring a reference:
For example:
-(void) someFunction:(int *)intPointer {
*intPointer = 5;
}
would be called as
int someInteger = 0;
[self someFunction: &someInteger];
// someInteger is now 5
The distinction between a pointer value and a true object reference can be seen in comparison to Swift which uses true references;
If I have
-(void)someFunction:(NSString *)someString {
int length = [someString length];
}
and then do
NSMutableArray *array = [NSMutableArray new];
[someFunction: (NSString *)array];
I will get a runtime exception since array doesn't have a length method, but the compiler can't confirm the type I am passing since it is a pointer.
If I attempted the equivalent in Swift then I will get a compile time error since it knows that the type coercion will always fail
All objects in Objective C passed by reference.
All C types such as NSUInteger, double etc. passed by value
C and Objective-C always pass parameters by value. Objective-C objects are always accessed through a reference (i.e. a pointer). There is a difference between a variable type (int, pointer, etc.) and the way variables are passed as function parameters. The use of the term reference in both scenarios can cause confusion.
by-value:
void f(int a) {
a = 14;
}
int a = 5;
NSLog(#"%d", a); // prints: 5
f(a);
NSLog(#"%d", a); // prints: 5
The value 5 is printed both times because the function f() is given a copy of the value of a, which is 5. The variable referenced within the function is not the same variable that was passed in; it is a copy.
In C++, you can have functions that take parameters by reference.
by-reference:
void f(int &a) {
a = 14;
}
int a = 5;
NSLog(#"%d", a); // prints: 5
f(a);
NSLog(#"%d", a); // prints: 14
Note the & in the function signature. In C++ (but not C, nor Objective-C), this means that the parameter is passed by reference. What this means is that a reference (pointer) to a is passed to the function. Within the function, the a variable is implicitly dereferenced (remember, it's really a pointer, but you don't treat it as one), and the original a variable declared outside the function is changed.
In C and Objective-C, passing a pointer to a function is functionally equivalent to using a reference parameter in C++. This is because a copy of the address is given to the function (remember, the parameter is still passed by value), and that address points to the same object instance that the original pointer does. The reason you don't see any explicit pointer dereferencing within the function (similar to the C++ reference) is because Objective-C syntax for object access always implicitly dereferences -- being within a function doesn't change this behavior.

How to convert switch state into integer in ios

I am using five switches for handling different types of notifications. To remember the state of the switch, I am thinking of converting state of five switches into an integer. For example, if my switches status is as follow, 01010 then the integer should be 10. Please help me how to achieve this.
At first extract each switch value and store it in a single string
Now convert the string to decimal /integer value like this:-
NSString * binarystring = #"01010";
long decimalValue = strtol([binarystring UTF8String], NULL, 2);
NSLog(#"%ld", decimalValue );
Edit
Get all switch control value in single string:-
NSString *binarystring = [[NSString alloc] initWithFormat:#"%i%i%i%i%i",self.switch1.isOn,self.switch2.isOn,self.switch3.isOn,self.switch4.isOn,self.switch5.isOn];
(Why bother encoding your 5 switch values into a single integer? Storing 5 Booleans is not hard. That said the question is how to do it...)
Important aside: BOOL values are not 0 and 1
Objective-C is a superset of C, and in the original C there was no Boolean type - instead it just used an integer type with the interpretation that 0 was false and anything else was true.
Objective-C defines BOOL as a signed char, that is an 8-bit signed integer type (as characters are just an integer type in C). So in Objective-C 0 is false, and -128..-1, 1..127 are all true. NO is defined as 0 and YES as 1, but various operations may result in other values.
To get a 0 or 1 from a BOOL b you can use the conditional operator:
b ? 1 : 0
However the built in logical operators by definition will always return 0 or 1 and never any of the other possible values. The ! operator is logical not, and two not's get you back to where you started so:
!!b
will also give you a 0 or 1.
In any code that takes a BOOL and tries to use it as a 0 or 1 you should really use one of the above (or an equivalent).
One way to solve it: using strings
Your question has been interpreted as using a string as an intermediary during the encoding. First assume the class has your five buttons stored in an instance variable as a simple array (it will allow us to loop):
const int kSWITCH_COUNT = 5; // let's not hard code it everywhere
#implemention MyClass
{
Switch *switches[kSWITCH_COUNT];
}
then the string method goes something like:
- (void) stringMethod
{
NSMutableString *binarystring = NSMutableString.new;
// build up the string one value at a time, note the !! so we only get 0 or 1 values
for (int ix = 0; ix < kSWITCH_COUNT; ix++)
[binarystring appendFormat:#"%d", !!switches[ix].isOn];
long decimalValue = strtol([binarystring UTF8String], NULL, 2);
NSLog(#"Encoded: 0x%lx", decimalValue);
}
This method works, but it is rather a circuitous way of getting to the result - you have 5 integer (Boolean) values and you want to combine them into an integer, why involve strings?
A better way to solve it: using integers
(Objective-)C provides bitwise operators to do shifts, or, and, etc. operations which treat integer types as an ordered collection of bits - which is what they are on a computer.
The << operator shifts left, e.g. 0x1 << 1 produces 0x2, i.e. << 1 is equivalent to multiplication by 2. The | operator is bitwise or, e.g. 0x1 << 1 | 1produces0x3`. The answer to your question now follows easily:
- (void) shiftMethod
{
unsigned int encoded = 0;
for (int ix = 0; ix < kSWITCH_COUNT; ix++)
encoded = (encoded << 1) | !!switches[ix].isOn;
NSLog(#"Encoded: 0x%x", encoded);
}
If you don't like shifts and ors you can use multiplication and addition:
encoded = encoded * 2 + !!switches[ix].isOn;
The above solves the problem directly, no converting to/from intermediate strings. It happens to be a lot faster as well, but in the overall scheme of an application neither approach is probably going to take a significant proportion of the execution time and you shouldn't select based on that.
A Third Way
If you are going to wish to set/get the individual bits of an integer a lot you can use struct types with bit-field widths. These let you set/get the bits of an integer directly - no shifting etc. required - and you may find them useful, but they are rather "low level". Any good book on C will show you how to use these.
HTH

not able to understand #encode and objCType

I came across a code snippet:
if(strcmp([obj objCType], #encode(BOOL))) == 0) where obj is a kind of NSNumber.
What exactly is happening here?
Break it up.
[obj objCType] returns a char * containing the Objective-C encoding for that NSValue.
NSNumber is a subclass of NSValue.
#encode(BOOL) does the same for the type BOOL.
strcmp() compares two string. If the string are equal it returns 0.
strcmp(…) == 0 returns true if strcmp() compared to equal strings.
I think you see where this is going: The condition returns true, if the NSValue obj has an encoding equal to the encoding of the type BOOL.

How do I use a core data Integer 64 property?

I want to have an Entity property in Core Data be a 64-bit integer. Since the model is going to run on iOS, and as far as I know these devices are not 64-bit, I figured that NSNumber was the way to go (core data gives you the option of objects or scalar properties for primitive types).
I'm assuming that NSNumber will internally take care of keeping track of a suitable representation for 64 bits.
Now, I need to subtract 1 from this "64 bit" property in my entity at some point (in case you didn't guess, the 64 bit property is the max_id parameter in the Twitter API), but to do so, I first need to unbox the number inside the NSNumber property.
So should i get the intValue? longValue? unsignedIntValue? unsignedLongValue? long long? which one?
Since you already know the type (64 bit integer), you don't need to check for it.
To get a 64 bit integer out of a NSNumber, do one of the following:
NSInteger myInteger = [myNSNumber integerValue];
int64_t myInteger = [myNSNumber integerValue];
In order to just add one to it, you can use something like this:
myNSNumber = [NSNumber numberWithInteger:[myNSNumber integerValue]+1]];
Note that iOS does have 64 bit data types like int64_t and NSInteger.
EDIT:
If the only reason that you are using NSNumber is to store the 64 bit integer, you can just declare the property like this in your model subclass and skip the unboxing/boxing altogether:
#property (nonatomic) int64_t myIntValue;
Note that core data does this by default if you select the Use scalar properties for primitive data types option of the Create NSManagedObject Subclass feature.
Try putting this in a NSNumber category:
-(int64_t) int64value
{
if (sizeof(short) == 8)
return [self shortValue];
if (sizeof(int) == 8)
return [self intValue];
if (sizeof(long) == 8)
return [self longValue];
if (sizeof(long long) == 8)
return [self longLongValue];
return -1; // or throw an exception
}
To get the C type contained in NSNumber use objCType
Example
NSNumber *myFloat = [NSNumber numberWithFloat:5.5f];
NSLog(#"%s", [myFloat objCType]);
Will print "f" as it contains a value of type float.
Also, check out #encode() which will return a C type character.
Example
NSNumber *myFloat = [NSNumber numberWithFloat:5.5f];
if (strcmp(myFloat) == #encode(float)) {
NSLog(#"This is a float");
}
Also
NSNumber *myFloat = [NSNumber numberWithFloat:5.5f];
CFNumberType numberType = CFNumberGetType((CFNumberRef)myFloat);

Resources