I have an objective-c/xcode project with several header and implementation files. I'd like to declare a variable that I can read and change from any file associated with the project. I tried doing extern int val = 0; in a header, but that lead to a linker error.
Would appreciate any help, thanks.
For storing and accessing an int in and iOS app, I recommend using NSUserDefaults.
You can set the value from anywhere in the application by
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:anInt forKey:#"IntKey"];
[defaults synchronize];
Then you can retrieve the value from anywhere in the application by
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int a = [defaults integerForKey:#"IntKey"];
This also works great for BOOL, float, NSString and NSArray. Check out the NSUserDefaults documentation for more details and examples.
Put the:
extern int val;
in at least one header file included by any .m or .c file where you want to use this global variable. Including an assignment here is almost always an error.
Put
int val = 0;
outside any function or method scope, in exactly one .m or .c file included in your build. No more.
If you access this variable often, and care about performance and battery life, using NSDefaults is several orders of magnitude slower than accessing a global variable. Using the app delegate for singleton model objects is also somewhat slower, and produces slightly larger apps.
Related
I want to store some data as a matrix of integers. For simplicity let's say I want to store this data in an array of integers. After each app launch I want to fill my model using this array. So the question is How to store arrays of integer in sandbox?
I don't want to write and rewrite data, maybe only once at first start.
What I tried:
I know about storing in plists and storing using NSCoding. But this strategies are used for more complicated models, not just arrays of integers.
EDIT:
is it faster to use c-arrays, storing them in txt-files and making own parser?
Plists and NSCoding are used for the simple integers as well.
However, you could just use the NSUserDefaults - that is the simples way:
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"AppData"];
Makes it easy to retrieve later from anywhere as well.
You can either store it in a plist, as you said, it's not a bad idea, it would work and do the job. It's not complicated at all, really, and not only used for complex models.
Else, you might want to use NSUserDefaults :
if (![NSUserDefaults standardUserDefaults] objectForKey:#"AKey"]) {
[[NSUserDefaults standardUserDefaults] setObject:yourArray forKey:#"AKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Don't forget that you need to wrap your integers into NSNumber.
A good practice for that would be using Objective-C's "new" notation : NSNumber * nb = #(42). That is much more readable that [NSNumber numberWithInt:42].
Good luck !
EDIT : According to your edit, no, don't use your own parser, at least for now. Don't try to optimize code when you don't really need it. Especially it it involves "breaking" Objective-C standards (and by that I mean using your own made stuff that might bug, and/or introduce strange behavior where Objective-C provides it's own way to do it). See this answer to know more about too much optimization.
+(void)storeArrayInDeviceWithKey:(NSArray *)arrData withKey:(NSString *)key{
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
[currentDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:arrData] forKey:key];
[currentDefaults synchronize];
}
For NSArray you need to little conversation with
NSKeyedArchiver archivedDataWithRootObject:UR OBJECT
What's the best way to get a value from NSUserDefaults on app load and store it in a global variable?
I could just hit [[NSUserDefaults standardUserDefaults] objectForKey:#"Theme"] every time I wanted to access the value stored, but it would hit the disk every time and that would be bad (I need the value for UITableView cells). What I'd like to do is store that value from it to a global variable on load, and then use that variable throughout the app.
What's the best way to do this? Obviously I can't make a constant because [[NSUserDefaults standardUserDefaults] objectForKey:#"Theme"] can't be compiled as a constant. How should I make a global variable for this? Is there any way to put it in the main.m or Prefix.pch file? I would hate to have to hit the App Delegate every time throughout my whole app.
"NSUserDefaults caches the information to avoid having to open the user’s defaults database each time you need a default value." - NSUserDefaults Class Reference
However, if you really want to have this 'constant' (it won't be a true constant though) do something like this
*.pch:
extern NSString *themeString;
AppDelegate.m:
NSString *themeString = nil;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ...
themeString = [[[NSUserDefaults standardUserDefaults] objectForKey:#"Theme"] copy];
// ...
}
Simple question about property lists within an iphone app. I know you can read data in from a plist, but is there a way to write user-inputted data to a plist? If so, how? It's easy to find tutorials on reading information from plists, but I'm having trouble finding resources on writing to plists.
This is how I write data items to a plist:
[myPlistFile setInteger: myInt forKey: #"someKey"];
Of course, you can change setInteger with setBool, etc for different types.
Hope this helps!
--
Edit:
If your .plist was a member of an important class or similar...
Header of myClass:
NSUserDefaults* myPreferences;
#property (nonatomic, retain) NSUserDefaults* myPreferences;
.m of myClass:
self.myPreferences = [NSUserDefaults standardUserDefaults]; // load our preferences
[[NSUserDefaults standardUserDefaults] registerDefaults: [NSDictionary dictionaryWithContentsOfFile: [[NSBundle mainBundle]pathForResource: #"nameOfFile" ofType: #"plist"]]]; // now load the custom .plist file
In the docs for both NSArray and NSDictionary it shows they each have an instance method:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
For NSDictionary it describes this method as
Writes a property list representation of the contents of the dictionary to a given path.
For NSArray it says this in the discussion
This method recursively validates that all the contained objects are property list objects before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
So essentially both of these will write plist's if the items that they contain can be used in plists e.g. Array, Dictionary, Boolean, Data, Date, Number and String
Problem
I want to store a NSString in NSUserDefaults and retrieve it later. I have a question about two different retrieving methods. Now at the top of the file I have:
// String used to identify the update object in the user defaults storage.
static NSString * const kLastStoreUpdateKey = #"LastStoreUpdate";
Method 1
NSString *lastUpdate = [[NSUserDefaults standardUserDefaults] objectForKey:kLastStoreUpdateKey];
Method 2
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *myString = [prefs stringForKey:kLastStoreUpdateKey];
Are there are significant differences I should know about? Also, can someone please explain what exactly is objectForKey? Apple's API states: that it "Returns the object associated with the first occurrence of the specified default." What exactly do they mean by the "specified default?
Thank you!
Generally you should use method 1.
that is "objectForKey".
Because, you know that, whatever you have stored in NSUserDefault. So, at the time of retriving it, you can catch the object with proper class like NSString, Array or any other user defined.
genrally "stringForKey" is not used.
If you are storing ingteger, BOOL into NSUserDefault then you should use intForKey, BOOLforKey, etc..
Cheers.
I'm confused about NSUserDefaults on the iPhone.
I come from a C / asm background and the Objective-C stuff has been a learning experience.
I'm currently using NSUserDefaults to store some strings (for the names in a highscore table). I want to start implementing a "Save Game" type feature so if the user gets a call or exits out of the game for a moment, they can come back to it.
To recover the game, I've got a couple of BOOL flags, a couple of ints to store some necessary variables, but I'm stuck at an array I need to store.
I have a 50 element array of unsigned chars. I could move it to ints if it would make things easier, but I'm just not seeing it.
To work with NSUserDefaults I can setBool (already doing that), setFloat (again, already doing that), setInteger, and setObject.
Obviously I could declare keys for each element of the array and store them one by one with setInteger but that's really kludgy. What's the best way to tackle this? Instead of an array of unsigned chars, I somehow try to use an NSObject? Are there any good tutorials on NSObjects that I can read through to understand it better?
Would Property Lists fit better with what you are trying to achieve?
You can create dictionary, store value for each setting or an array, and then dump it to Property List. You can easily read them back or update the values based on keys.
http://developer.apple.com/documentation/Cocoa/Conceptual/PropertyLists/...
You can pass an NSArray or an NSDictionary to NSUserDefaults with setObject:forKey:. It works just fine. Obviously, your array of unsigned chars would have to become an an NSArray of NSNumbers, which implies some overhead, but it's probably your easiest option.
Try using an NSData object, which can be directly stored to NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:[NSData dataWithBytes:array length:len]
forKey:#"someArray"];
Whoa there, you should really look into using Core Data (or just sqllite) to store game state. NSUserDefaults is for what is says - defaults, configuration settings. It is not really meant to store largish chunks of binary data.
To convert a 50 element C array of unsigned chars in to an NSArray which you can store in a NSUserDefaults using:
NSMutableArray* a = [NSMutableArray new];
for ( int i = 0; i < 50; ++i ) {
[a addObject:[NSNumber numberWithInteger:yourArray[i]]];
}
[[NSUserDefaults standardUserDefaults] setObject:a forKey:#"theArray"];
[a release];
to get it back:
NSArray* a = [[NSUserDefaults standardUserDefaults] objectForKey:#"theArray"];
for ( int i = 0; i < 50; ++i ) {
yourArray[i] = [[a objectAtIndex:i] integerValue];
}
There is certainly no problem with using NSUserDefaults to store your game state, not when it is so small. If your game state was humoungous, you might want to investiage storing it as a plist file, but NSUserDefaults will not break a sweat handling this trivial amount of data.