The following is returning an error because evidently I cannot compare an NSInteger or int to nil. Can anyone suggest syntax to do this
NSInteger *lastID = [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"]==nil ? #1 : [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"];
NSInteger lastID = [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"] == 0 ? 1 : [[NSUserDefaults standardUserDefaults] integerForKey:#"lastID"];
NSInteger is a primitive type. nil is for objects. 0 check, checks is your userDefaults' integer value assigned a value. If it didn't assigned, it returns 0.
NSInteger is a primitive type, not an object. I'm quite sure that an NSInteger* is not what you want, but an NSInteger. integerForKey returns an NSInteger, not an NSInteger*.
NSInteger is a primitive, not an NSObject, so any hypothetical comparison to nil would effectively be comparing to numeric zero.
Since you are using the ternary operator to assign lastID to the boxed NSNumber #1 if the result would be nil, then it seems what you want to do is store an NSNumber in the defaults, use objectForKey: to retrieve it, and use integerValue on the retrieved NSNumber to get the primitive value.
If you do indeed want to store primitives, that's fine, but you'll need to change your logic to not expect nil, which is only pertinent to sublcasses of NSObject, basically.
UPDATE: an example app.
#import <Foundation/Foundation.h>
enum {
MyIdTypeUnrecognized = 0,
MyIdTypeInvalid = 1
} myIdType;
int main(int argc, char *argv[]) {
#autoreleasepool {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *key = #"lastID";
NSInteger lastID = -100;
[defaults removeObjectForKey:key];
NSLog(#"Case 1: should find 0, and set lastID to 1.");
lastID = [defaults integerForKey:key];
lastID = (lastID == MyIdTypeUnrecognized) ? MyIdTypeInvalid : lastID;
NSLog(#"--- lastID:%ld", lastID);
NSLog(#"Case 2: Set lastID to 2, then try to fetch.");
lastID = -100;
[defaults setInteger:2 forKey:key];
[defaults synchronize];
lastID = [defaults integerForKey:key];
lastID = (lastID == MyIdTypeUnrecognized) ? MyIdTypeInvalid : lastID;
NSLog(#"--- lastID:%ld", lastID);
}
}
//2015-09-04 10:04:38.075 nsnotfound[66983:5231434] Case 1: should find 0, and set lastID to 1.
//2015-09-04 10:04:38.076 nsnotfound[66983:5231434] --- lastID:1
//2015-09-04 10:04:38.076 nsnotfound[66983:5231434] Case 2: Set lastID to 2, then try to fetch.
//2015-09-04 10:04:38.076 nsnotfound[66983:5231434] --- lastID:2
I suggested using NSNumber earlier, as your question used a boxed NSNumber -- #1. #1 is simply shorthand for [NSNumber numberWithInteger:1]. If you don't want to use objects, you can definitely use primitives.
The example above uses both zero and one as sentinel values for your "unfound" and "invalid" IDs. All other IDs should be usable by your app logic. I hope this helps.
You can retrieve this number to NSNumber which is an object and that way you can check if it is nil or not.
NSInteger is basically a typecast of Int in 32 bit or long in 64 bit, as you compare int in similar way you can compare NSInteger.It will never be nil.
NSInteger isn't an object, its a primitive type stored as a NSNumber inside NSUserDefaults. Checking for existence of primitive types is a bit tricky, as its non-existence is equivalent to nil or 0. The cleanest way to get its value and get a default value if it doesn't exist is probably this way:
NSNumber *lastID = [[NSUserDefaults standardUserDefaults] objectForKey:#"lastID"] ?: #1;
This would return you lastID if it exists, otherwise you get a NSNumber with a value of 1.
Related
I used NSUserDefaults to store an integer value. When I run the project there's a default value of 0 in it. How do I remove the 0 value? I just want it to be empty until the user put something in it.
Here is my code:
- (IBAction)save:(id)sender{
int port = [[portField text] integerValue];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:port forKey:#"port"];
[defaults synchronize];
}
- (void)viewDidLoad {
int port = [defaults integerForKey:#"port"];
NSString *portString = [NSString stringWithFormat:#"%lu",(unsigned long)port];
portField.text = portString;
[super viewDidLoad];
}
Well, this line:
int port = [defaults integerForKey:#"port"];
will always assign some value to port.
Also, this:
NSString *portString = [NSString stringWithFormat:#"%lu",(unsigned long)port];
will always produce a non-empty string. There's no value you could pass for a %lu specifier that will produce an empty string.
So, what you want to do is ask for the object that's stored in the preferences, without converting it to a integer value:
id portObject = [defaults objectForKey:#"port"];
if (portObject)
{
NSInteger port;
if ([portObject respondsToSelector:#selector(integerValue)])
port = [portObject integerValue];
else
{
// portObject is some unexpected class of object, such as an array or dictionary. Take some appropriate action. Or:
port = 0;
}
portField.text = [NSString stringWithFormat:#"%ld", (long)port];
}
else
{
// There was no value either stored or registered for the key "port"
portField.text = #"";
}
Note that for this to work, you must not have registered a default value for the "port" key using -[NSUserDefaults registerDefaults:].
As far as I know, NSUserDefaults always return default values for primitive types when they are not exists.
If the default value is a valid value you use, I suggest to put a blank object or just NSString object to determine if the associated value is valid. e.g.
NSObject *objValid = [userDefaults objectForKey:#"integerIsValid"];
if(objValid != nil) {
// User stored integer value exists.
NSInteger i = [userDefaults integerForKey:#"SomeInteger"];
} else {
// Have not saved integer value.
}
When user save the integer value, also save the associated object:
[userDefaults setObject:[[NSObject alloc]init] forKey:#"integerIsValid"];
[userDefaults setInteger:i forKey:#"SomeInteger"];
UPDATE
If your value range can transform to another value range by some rules, then you can do it another way.
All values are in range [0, 100) for example, you can store in range [1, 101) and retrieve the real value by subtracting 1.
I'm trying to (in Xcode5) use the 'removeObjectAtIndex' for an 'MutableArray' which takes an NSUInteger but the variable I'm using is an integer so I casted with (NSUInteger *) but I get a warning that says cast to 'NSUInteger *' (aka unsigned long *) from smaller integer type. I have not casted the variable 'second' in the code to keep the warning there but it is also an integer
-(void) moveObjectAtIndex:(NSUInteger *)oldIndex toNewIndex:(NSUInteger *)newIndex{
id *member = [self.array objectAtIndex:*oldIndex];
[self.array removeObjectAtIndex:*oldIndex];
if ((NSInteger)newIndex >=(self.array.count)) {
newIndex--; //i casted newIndex because I got a warning about ordered comparison of NSUInteger with NSInteger (I'm not sure if this is best solution)
}
[self.array insertObject:member atIndex: *newIndex];
}
-(void)moveObjectInArray:(NSMutableArray *)array{
[array moveObjectAtIndex:(NSUInteger *) first toNewIndex:second];
}
Your use of pointers is all wonky. id* should just be id and NSUInteger* should just be NSUInteger — you don't want a pointer to a pointer to an object or a pointer to an integer here.
What's problem for you of using just NSUInteger rather than NSUInteger* ?
-(void) moveObjectAtIndex:(NSUInteger)oldIndex toNewIndex:(NSUInteger)newIndex{
id member = [self.array objectAtIndex:oldIndex]; //Here is id, id* is wrong
[self.array removeObjectAtIndex:oldIndex];
newIndex = newIndex >= self.array.count ? : self.array.count - 1; // Here should be self.array.count - 1, not newIndex-1
newIndex = MIN(_cloudListArray.count, newIndex);
[self.array insertObject:member atIndex:newIndex];
}
In the following statement
if ((NSInteger)newIndex >=(self.array.count))
you are typecasting pointer to NSInteger. It should be
if ((NSInteger)*newIndex >=(self.array.count))
Still, you should be careful with typecasting and be wary of their consequences due to any signed/unsigned conversion or data loss.
Also, in this line
[array moveObjectAtIndex:(NSUInteger *) first toNewIndex:second];
type of first should be NSUInteger * or of same size. Please note that long is 64-bit in 64-bit environment and typecasting as smaller pointer type to larger pointer type will yield undefined behaviour. Same applies to second. One solution is that use temporary variable and then copy back the result.
this is the code:
NSNumber *taskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
self.taskList is an NSArray which filled with core data fetch request in ViewController's viewDidLoad method.
the taskId is: 1
the intTaskId is: 269303816
In actually, the value stored in core data is: 1
below is my questions:
1, I am confused why the NSInteger incorrect?
2, Should I have to replace NSInteger with NSNumber to avoid any other problems?
NSNumber is an object, whereas NSInteger is simply a typedef for a primitive (non-object) type (like int). NSInteger is not a subclass of NSNumber. Core Data returns numbers as instances of NSNumber. You're getting the weird NSInteger value because it's pointing to an object of type NSNumber but attempting to print it as if it were just an integer.
You'll need to replace NSInteger with NSNumber to avoid any problems. You could also use the intValue method on NSNumber to get back an NSInteger:
NSNumber *objTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [objTaskId intValue];
You'll need to do this if you want to do comparisons (greater than, equal too, smaller than) or arithmetic (you can't add an NSNumber to another NSNumber or an NSNumber to a primitive type like an int or float).
I'm trying to access an array using another array integer value as an index.
NSInteger index=[appDelegate.randomRiddles objectAtIndex:appDelegate.randomRiddlesCounter];
int i = index;
questionText.text = [[appDelegate.currentRiddlesContent objectAtIndex:i] objectForKey:#"question"];
//where appDelegate.randomRiddlesCounter is an NSInteger and appDelegate.randomRiddles is a NSMutableArray
However I'm getting incompatible pointer to int conversion warning. How can I fix this above code? The warning I get is coming from the first line.
Try:
NSNumber *index = [appDelegate.randomRiddles objectAtIndex: appDelegate.randomRiddlesCounter];
int i = [index intValue];
questionText.text = [[appDelegate.currentRiddlesContent objectAtIndex: i] objectForKey: #"question"];
NSInteger is an integral type, not an object.
Try this:
int i = [index intValue];
An NSArray like object can only store Objective-C object pointers (i.e. everything that you can assign to an id)
With objectAtIndex you get the object, with indexOfObject:(id)anObject you get the corresponding index.
These two instructions are both valid:
id bla = [appDelegate.randomRiddles objectAtIndex:appDelegate.randomRiddlesCounter];
NSInteger index = [appDelegate.randomRiddles indexOfObject:myObject];
The second assumes that myObject is at least of type id
So you try to convert a pointer to an int. Therefore the warning is issued.
I have the following code;
int days = [[SettingsUtils daysToRetainHistory] intValue];
[retainHistory setText:[NSString stringWithFormat:#"Days to retain History: %d", days]];
[daysToRetainHistory setValue:days animated:NO];
where [SettingsUtils daysToRetainHistory] is as follows;
+ (int) daysToRetainHistory {
return (int)[[NSUserDefaults standardUserDefaults] objectForKey:#"CaseBaseDaysToRetainHistory"];
}
I get the compiler warning Invalid receiver type 'int' because I call intValue on an int but unless I do this I can't seem to get the integer value out and always end up with the memory address i.e. 98765432 instead of 9 which ruins the UILabel display [retainHistory] and the UISlider [daysToRetainHistory] value.
How do I avoid the compiler warning and still get my integer value in the label and the necessary float value for setting the UISlider value?
It's because you are casting a memory address to an int that int value isn't working. Instead just use the method you want from NSUserDefaults:
+ (NSInteger)daysToRetainHistory
{
return [[NSUserDefaults standardUserDefaults] integerForKey:#"CaseBaseDaysToRetainHistory"];
}
then just use it directly:
NSInteger days = [SettingsUtils daysToRetainHistory];
You should also store your days with the same method:
[[NSUserDefaults standardUserDefaults] setInteger:days forKey:#"CaseBaseDaysToRetainHistory"];
Although if you set the NSNumber object and it actually does contain an integer, that will work fine too.