Proper updating of a 32bit method to 64bit support - ios

I have a method I am trying to update to 64-bit support for my app. I have lots of these warnings.
The issue is with the below example. I get the warning:
Implicit conversion loses integer precision NSInteger to int
- (int) aMethod {
NSUserDefaults *u = [NSUserDefaults standardUserDefaults];
return [u integerForKey:#"someKey"];
}
If I change the return type to NSInteger I get
Conflicting return type in implementation of aMethod int vs NSInteger aka long
-(NSInteger) aMethod {
NSUserDefaults *u = [NSUserDefaults standardUserDefaults];
return [u integerForKey:#"someKey"];
}
I have tried casting the return type
return (NSInteger)[u integerForKey:#"someKey"];
or
return (long)[u integerForKey:#"someKey"];
But I can't get the error to go away unless I cast it to (int)
return (int)[u integerForKey:#"someKey"];
I have read many SO questions but I can't seem to get a good answer.
What is the proper way of updating this method?

The method:
- (int) aMethod ...
returns a value of type int. The method may first declared, probably in a header (.h) file, and then defined in an implementation (.m) file.
The statement:
return [u integerForKey:#"someKey"];
returns the result of calling integerForKey:, which is a value of type NSInteger.
NSInteger is not a type defined by the (Objective-)C language, rather it is a type alias (typedef) defined in the frameworks intended to represent the "natural" integer size on the platform. It is an alias for int on 32-bit platforms and long on 64-bit.
So your compiler error is telling you that the (now) 64-bit value is being truncated to fit a 32-bit result. There is no single right way to fix this, it depends on what the use of the value associated with #someKey is:
If the value should now be 64-bit then change the return type of aMethod; both in its separate declaration, if it has one, and in its implementation; to NSInteger. You will also need to check that wherever the method is called that returning an NSInteger value is acceptable, and you may need to make follow-on changes to variable types etc. You also need to look at places where the value associated with the key is set.
If the value associated with the key may remain as 32-bit then you can use intForKey: instead of integerForKey and leave the return type as is. You should also change where the key is set to store an int
HTH
Correction
There is (no longer?) a convenience method intForKey: on NSUserDefaults. Alternatives include: integerForKey: and casting; objectForKey: to obtain an NSNumber reference and then intValue; etc.

Related

iOS/core-data: What is best way to check if there is a value for an attribute in core data?

There are a lot of variants of this question asked but none seem to quite get what I need.
I have an object, contact, which is in a core data entity contacts. I want to display as much of the address as is present so if there is no zip for example for example, I will leave that out.
I have tried variants of the following without any success:
if (self.contact.state && !self.contact.zip) {
NSString *location = contact.state;
}
and also
self.contactZip.text = self.contact.zip;
if (length(self.contactZip.text)<1) {
NSString *location = contact.state;
}
Can anyone suggest right way to do this?
Thx.
The way to check for the presence of a value with Core Data (as with any Objective-C property) is to check and see if the value is nil:
if (self.contact.zip == nil) {
...
}
Although the ! syntax is common, it's better to write the code to specifically check for nil [Partly because it makes your code clearer about what it's doing. Partly because ! is a boolean operator that only happens to work when checking for nil because of implementation details of the language.].
If the property is a string and what you actually want to know is whether it has non-zero length, check both (because a zero-length string is not the same as a nil value). In Objective-C you can take advantage of the language's treatment of nil method calls and do
if ([self.contact.zip length] == 0) {
...
}
Calling length on a nil argument will return zero, so in ObjC this ends up checking both for nil and for zero length at the same time.

Obj-C setValuesForKeysWithDictionary 64-bit vs 32-bit

I am pulling a JSON object from my API and creating it using the following code (in hindsight not the way to go):
+ (YActivity *)instanceFromDictionary:(NSDictionary *)jsonDictionary
{
YActivity * instance = [[YActivity alloc] init];
[instance setAttributesFromDictionary:jsonDictionary];
return instance;
}
- (void)setAttributesFromDictionary:(NSDictionary *)jsonDictionary
{
if (![jsonDictionary isKindOfClass:[NSDictionary class]]) {
return;
}
[self setValuesForKeysWithDictionary:jsonDictionary];
}
One of the keys is "type". I have a read-only variable #synthesized called "type". On the 32-bit version of my app, this is set right away before setValue:(id)value forUndefinedKey:(NSString *)key is called. I reference this value in that method, and on the 64-bit version of my app, when the breakpoint hits this method, type is not set yet.
Clearly this isn't the best course of action. I am just wondering if anyone else as seen this or if I'm barking up the wrong tree. I diffed the files between the two versions and they are identical. I am running them both on iOS 8.1 Simulator, the API is returning the same thing for both...I'm stumped. Basically on the old version defined keys are set before undefined, and on the new version it seems the opposite of that.
NSDictionary objects are unordered collections, so code should never make assumptions about the order in which a dictionary will enumerate its own keys. It turns out that there are implementation differences between the 32- and 64-bit runtimes that affect where hashed values end up being stored.
Since the API contract explicitly doesn't guarantee order, that shouldn't cause problems, but it can (and in this case apparently does) have the side-effect of causing code that formerly 'worked' to break when compiled for the 64-bit architecture.
A quick way to fix the problem you're currently having without significantly changing the implementation would be to enumerate the dictionary's keys yourself, which would allow you to provide an array of keys ordered however you wish:
- (void)setAttributesFromDictionary:(NSDictionary *)dictionary
{
// So instead of doing this...
//
// [self setValuesForKeysWithDictionary:dictionary];
// You could do something along these lines:
//
NSMutableArray *keys = dictionary.allKeys.mutableCopy;
// TODO: insert code to change the order of the keys array.
// Then loop through the keys yourself...
for (NSString *key in keys)
{
[self setValue:dictionary[key] forKey:key];
}
}

NSKeyedArchiver changing from objectForKey to intForKey

I have an app which stores data using NSKeyedArchiver and everything was working fine.
In the first version of the app a value X was stored as a string, but now I changed it to be stored and saved as an int. The problem is if someone updates from the old version to the newest the app crashes because intForKey: gets called on a key containing a String.
Is there some way during decoding to check if what is being decoded is an int or an object?
NSKeyedArchiver doesn't actually encode primitive values. Instead, it wraps them in an NSNumber.
Perhaps, have you tried using decodeObjectForKey: instead of decodeIntegerForKey:?
In example,
id xObject = [decoder decodeObjectForKey:#"xObjectKey"];
self.x = [xObject integerValue];
// I believe this should work because xObject will either by an NSNumber or NSString
// Both of which respond to the selector integerValue

UITextfield to NSUserDefaults type error

I have a UITextField and I wish to extract the integer value from it and put it into a NSUserdefaults.
#property (weak, nonatomic) IBOutlet UITextField *defaultTipPercentage;
[defaults setInteger: defaultTipPercentage forKey:#"another_key_that_you_choose"];
I'm getting an error saying
Use of undeclared identifer 'defaultTipPercentage'. did you mean '_defaultTipPercentage?'
I tried this but I get an error
Incompatible pointer to integer conversion sending 'UITextField *_weak' to parameter of type 'NSInteger'(aka 'Int')
Pretty much just incompatible types. I was wondering what would be a quick fix for this
You have 2 problems.
You can't write a text field directly to user defaults - you need the text, or rather, the integer value of the text.
With a property, you either need to refer to it using self.defaultTipPercentage (which uses the getter), or _defaultTipPercentage, which refers to the backing instance variable directly. Using self.xxx syntax is generally safer.
Jenox already posted code to solve the first problem in the comments.
To also fix the second problem, you need to use property list syntax:
NSInteger tip = [self.defaultTipPercentage.text integerValue];
[defaults setInteger:tip forKey:#"another_key_that_you_choose"];
NSInteger tip = [self.defaultTipPercentage.text integerValue];
[defaults setInteger:tip forKey:#"another_key_that_you_choose"];

If Statement Failing sometimes with id BoolValue Comparison?

I am pulling data from the web that is formatted in JSON and when I parse the data using "ValueForKeyPath" it stores the string value as an id object.
So I store the data into a NSMutableArray
In the debugger window it shows all the elements added as (id) null.
I have an if statement
if ([[self.activeCategories objectAtIndex:selected] boolValue] == true)
Sometimes I would say 20% of the time it fails the if statement when it should not.
I was wondering if it was because the self.activeCategories is storing id types. Do I need to do [NSString stringWithFormat#"%#", x] to all the objects inside the array? It seems like when I just straight cast it by using (NSString *) it is still type id in the debugger.
It's a very strange error to me... as the error is not consistently reproducible.
Try it like that:
if ([[self.activeCategories objectAtIndex:selected] boolValue])
According to that article a BOOL may hold values other than 0 and 1 which may fail the comparison.

Resources