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

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.

Related

Resolve numbers and bools in nested NSDictionary

Let's say I loaded a JSON string into an NSDictionary that had some numbers written as strings. The resulting NSDictionary might look something like this:
NSDictionary* example = #{
#"aNumber": #"1",
#"aFloat": #"2.9708",
#"aBool": #"true",
#"aNestedDict": #{
#"more": #"220",
#"evenMore": #"false",
#"anArray": #[
#"1",
#"2"
]
}
};
I want to parse the float, integer, and bool ('true', 'false', 'yes', 'no' - case insensitive) values into their respective Objective-c class types. I've looked around, but can't find any examples of built in APIs to do this.
(Enlarged since people aren't reading the question)
Am I stuck writing a recursive parser and converting each value manually, or does Apple offer a built-in API to recursively parse it for me?
There isn't an API to do it, however you can make a helper function to figure it out. The API that apple does provide however are helper functions on NSString, i.e.: .integerValue, .doubleValue, .boolValue. However not only is this limited to NSString, it's also not comprehensive / intelligent.
So if you want to parse the string into a variable of type BOOL you can do something as simple as:
- (NSNumber *)parseBool:(NSString *)value
{
if( [value caseInsensitiveCompare:#"yes"] == NSOrderedSame || [value caseInsensitiveCompare:#"true"] == NSOrderedSame )
{
return #YES;
} else if ([value caseInsensitiveCompare:#"no"] == NSOrderedSame || [value caseInsensitiveCompare:#"false"] == NSOrderedSame )
{
return #NO;
} else
{
return nil;
}
}
EDIT:
For int and double just use:
NSString *string = #"1";
NSInteger intValue = string.integerValue;
double doubleValue = string.doubleValue;
JSON supports strings, numbers with and without decimals, boolean values, null values, dictionaries and arrays. So anyone wanting to represent numbers and boolean values in JSON can just do that.
Anyone producing JSON should document what they are producing. So if they insist on representing a boolean value as a string, then they should document which possible strings will be used to represent true and false. And then it's just a matter of string comparison.
For numbers stored as string, you can use integerValue or doubleValue which works just fine for strings.

Determine if NSUInteger has a value or is nil

I'm trying to use a HKAnchoredObjectQuery on iOS.
To do so I have to provide an anchor to the query which in this case is a NSUInteger. I want to save this integer for queries in the future in the NSUserDefaults and just simply want to use
if(anchor != nil) {
// do stuff
}
But the complier tells me that I'm not allowed to do this because I'm comparing a integer to a pointer and I understand why I'm not able to do this but I don't know how to do it.
The error code is this
Comparison between pointer and integer ('NSUInteger' (aka 'unsigned long') and 'void *')
Does anyone know how I'm able to determine if my anchor has a value or not?
NSUInteger is a primitive type, not an object type. NSNumber is an object type. For an NSNumber object, you can check whether it is nil or not. An NSUInteger is never nil because it is an integer, not a pointer.
Many functions that return NSUInteger as a result for a search return the constant NSNotFound if the search failed. NSNotFound is defined as a very large integer (2^31 - 1 for 32 bit, 2^63 - 1) for 64 bit).
NSUInteger is not an object and cannot be compared to nil. In over-simplistic terms, an object is a pointer to a memory location that may contain an value or be nil. NSUInteger is just a value. It may be 0, or some other value that you don't expect, but it will have a value.
An NSUInteger cannot be nil because it is not an object. The default value on creation would be zero. To save in NSUserDefaults, you could store it as an NSNumber, which could be checked for nil later.
To store it in NSUserDefaults:
NSNumber *aNumber = #(anchor);
[[NSUserDefaults standardUserDefaults] setObject:aNumber forKey:aKey];
To get the anchor back from NSUserDefaults:
NSNumber *aNumber = [[NSUserDefaults standardUserDefaults] objectForKey:aKey];
if (aNumber != nil) {
NSUInteger anchor = [aNumber integerValue];
}
NSUInteger or simply int are basically primitive data types and they should be compared with the integer values with in the range of the corresponding data types. nil is used with referenced objects.
In your case, as anchor is NSUInteger so you should use integer value for comparison in place of nil as-
if(anchor != 0) {
// do stuff
}

iOS converting value in NSDictionary with (int) fail

I had a NSDictionary contains 2 key/value pairs:
NSDictionary *dic = #{#"tag":#2, //NSNumber
#"string":#"someString"}; //NSString
NSLog(#"%i",(int)[dic objectForKey:#"tag"]); //print out 34
NSLog(#"%i",[dic objectForKey:#"tag"] intValue]); //print out 2
Why does "converting id value to int with (int)"get me the wrong result but not the other way? are they in different levels of conversion?
Why does "converting id value to int with (int)"get me the wrong result but not the other way? are they in different levels of conversion?
id is a pointer type. id pointers point to Objective-C objects in memory. By casting id to (int), you are merely reinterpreting (some of) the pointer's bit pattern as an int, which is quite meaningless. You have to call the proper conversion methods of NSString and NSNumber if you want to reliably get the primitive values out of the Objective-C object.
If you ever seemingly get the "correct" value of 2 in the case of pointer-casting with NSNumber, that may be because the Objective-C runtime makes use of an optimization technique called tagged pointers, whereby small objects are not really created and allocated, but their semantics (the number's bits which the NSNumber object stores) is stuffed into the unused bits of the pointer.
#2 is not an int but a NSNumber you can't cast an NSNumber into an int. You have to use intValue method to get the correct result.
The method objectForKey: returns a pointer to the NSNumber object #2, not the value stored in the object itself. So you're typecasting the pointer, not the value 2. In the last line you don't typecast the object but you access a property called intValue which returns the value of the object expressed as an int.
NSDictionary contains Object with Key value pairs,but you passed int(#2) into object
NSDictionary *dic = #{#"tag":#2, //NSNumber
#"string":#"someString"};
so Change int to NSNumber like
NSDictionary *dic = #{#"tag":[NSNumber numberWithInt:2];,#"string":#"someString"};
and you can get it..
int number = [[dict objectForKey:#"tag"] intValue];

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.

how to handle boolean value with AFNetworking parsing JSON

I have some JSON that comes back like this:
"items":[
{
"has_instore_image": false
}
]
If I output the value like this:
NSLog(#"has_instore_image val: %#", [item objectForKey:#"has_instore_image"]);
I get
has_instore_image val: 0
but if I test like this:
if([item objectForKey:#"has_instore_image"]==0){
NSLog(#"no, there is not an instore image");
}else{
...
It always goes to the else statement... hmmm.. How would you suggest I get the BOOL value and test? I've read through the BOOL questions here and am just confused this is not working as I'd anticipate.
thx
NSDictionary's instance method objectForKey returns an id, not a primitive value.
If it's a boolean, int, float, etc number-like value in JSON, it will serialized to an NSNumber by Apple's NSJSONSerialization class and most/all other common JSON parsers in iOS.
If you want to get the BOOL value out of it, you can do something like this:
BOOL has_instore_image = [[item objectForKey:#"has_instore_image"] boolValue];
You are comparing a pointer with an integer here
[item objectForKey:#"has_instore_image"]==0
You should use
[item objectForKey:#"has_instore_image"].integerValue==0
Also note that a BOOL of NO equals 0.
The NSLogstatement in your code prints a 0, but only because if you give NSLogan object as parameter, the objects descriptionis called.
i will suggest to hold these id type (returned from dictionary) to NSNumber .
NSNumber *boolNum=(NSNumber*)[item objectForKey:#"has_instore_image"];
after that you can get bool value from boolNum
[boolNum boolValue]
try this
if([boolNum boolValue]==NO){
NSLog(#"no, there is not an instore image");
}else
{
}

Resources