Accessing NSUserDefaults Often - objective-c

During a logic process in my app, I need to access the user preferences frequently, and a bunch of times 10~15 to determine what needs to be processed and how. May this question is not about performance, but about doing it correctly.
Currently I'm doing a [[NSUserDefaults standardUserDefaults] valueForKey:...] each time I need to request a value. Is this correct? I think that "saving" the user defaults as an ivar could reduce extra work, but then I wonder if this won't have sync problems, like if the user changes the preferences and they get updated only if the app is restarted (so the user defaults object is recreated).
Is there any better way?

Don't worry about it, it's extremely fast and I do not believe there is a better way, it's the way the class is meant to be used.
The NSUserDefaults class caches the values internally so the lookup is extremely fast. The overhead of [NSUserDefaults standardUserDefaults] vs an instance variable is so small that you wouldn't even notice it if you did it 5 million times in your code.
The only proper way of optimising this would be by improving your logic, caching the values you're using yourself with a pointer rather than the dictionary that NSUserDefaults basically is etc.

You won't have any problem if you save the defaults object to an ivar. Notice it's a singleton and its pointer won't change.

Do the values in the user defaults change over time during this logic process?
If not, you could access each value that you'll need throughout the process once at the start and store the results in local variables.
Then you can use those variables as many times as you like without having to hit the user defaults reading the data each time.
However, if those values are being changed while your logic process is ongoing, then accessing them from the defaults is probably the only way.
In terms of performance, accessing it 10-15 times isn't going to have any adverse effect. If you were accessing it 10-15 times per second for a prolonged period of time, then you might encounter some responsiveness issues.

Related

Strategy for a self-retaining and self-releasing object

I need to implement a bit of functionality that can be used from a few different places in an application. It's basically sending something over the network, but I don't need it to be attached to any particular view - I can communicate everything to the user by UIAlertViews.
What I would like to do is encapsulating the functionality in an object (?) that can maintain it's own state for a while and then disappear all by itself. I've read in several similar topics that it's generally not advised to have an object that retains and then releases itself, but on the other hand you have singletons which apart from the fact that they never get released, are very similar in nature. You don't need to keep reference to them just to use them properly. In my situation however I feel it woud be somewhat wasteful to create a singleton and then keep it alive for something that takes a few seconds to execute.
What I came up with is a static dictionary local to the class, that keeps unique references to the instances of the class, and then, when an instance is done with its task, it performs selector 'removeObjectForKey' after delay which removes the only existing reference and effectively kills the object. This way I keep only a dictionary in memory which for the most time is empty anyway.
The question is: are there any unexpected side effects of such a solution that I should be aware of and are there any other good patterns for described situation?
So basically instead of a persistent object of your own class, you've got a persistent object of type NSDictionary? How does that help matters? Is your object unusually large? If you are making your codebase more complicated for the sake of a few bytes, that's not a good tradeoff.
Especially now ARC is commonplace, this kind of trickery is usually not a good idea. Have you measured how much memory a singleton approach takes and found it to be a problem? Unless you have done this, use a singleton. It's simpler code, and all other things being equal, simpler code is far better.

Make NSDocument "edited" when a binded control changes

I have an array of NSDictionaries and a NSDictionary iVar (*selectedDictionary) that points to one of the array's objects. *selectedDictionary points to a different object every time the user selects a different row in a NSTableView. Several GUI controls are binded to the selectedDictionary instance (IB).
I just want to make the NSDocument dirty (edited) every time the user alters the above controls. I think using Key Value Observing for ALL the objects in the array and all their kaypaths, is a bit insufficient. Any suggestions?
Thanks
NSDocument's support for marking a document as dirty comes directly from the NSUndoManager. The easiest way to change the document to dirty is to do an implementation of Undo, and this is basically going to mean doing the undo inside of the model class that the document is using (or the subclass of NSDocument if you choose to handle all storage directly in there).
Apple has documentation on this here:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/UndoArchitecture/Articles/AppKitUndo.html
Since you indicate you have an array of dictionaries, that's going to make it a bit more work to implement, but once you have nailed that, you'll be in good shape.
Alternatively, if you don't want to go with the freebie support provided by NSDocument and NSUndoManager, you can manually handle undo and use the updateChangeCount: method to modify the internal understanding of whether changes have occurred. This takes some work, and likely is a lot less useful than just setting up undo correctly.
As for the efficiency of observing all the objects in the array, I wouldn't worry about it unless you have profiled it and found it to be inefficient. KVO is really pretty darned efficient and we regularly observe multiple values in every element of arrays without seeing performance problems. You have to observe the array itself in order to handle adds and removes (assuming your arrays have this).
As far as I can tell, though, you have a selectedDictionary which is used to determine the other controls that are shown. In this case, you can use KVO to observe the value of selectedDictionary and when it changes, you can remove the observers from the previous selectedDictionary and add them to the keys in the current selectedDictionary. This is basically what bindings is doing in order to handle the display and setting, anyway.
One other consideration that I've used in the past is referenced in this StackOverflow post:
NSMutableDictionary KVO. If you look at my answer here, I outline a trick for getting notifications when a new key is added or an existing key is deleted. It also has the benefit of giving you a notification when there's any change. It's not always a great solution, but it does save some effort on coding the list of keys to observe.
Beyond that, you'll have to add every key you're expecting to have an effect on the saved state of the document.

NSUserDefaults, is a good practice to create local value too?

I'd like to know if creating a mirror of values saved in UserDefault is a good practice or not?
For example if I store parameter "A" in UserDefault is it useful to maintain a copy of this value locally and update it when UserDefault changes?
In my app I had the necessity to frequently read parameter "A", and in this case I think it's better to read from an ivar instead of from a NSUserDefault, but I'm not sure about that so I'm asking if there is a good practice to follow.
Seems like extra work and a premature optimization to me.
In all my apps I've always retrieved the NSUserDefaults each time they were needed and it was never an issue. Keeps the code short and obvious. And if it becomes a problem, you fix it, easy as that.
The performance is irrelevant because NSUserDefaults is not intended to hold much information or be used so often as to cause a performance problem.
As for code encapsulation and reuse, don't use NSUserDefaults directly on code that performs specific tasks unrelated to your preferences. eg: a method to colorize an image should take the user preference as a parameter instead. Use it directly anywhere else.

Odd NSCache Eviction Behaviour

I've been using an NSCache to store items that should be cached for performance reasons, since they are rather expensive to recreate. Understanding NSCache behaviour is giving me some headaches though.
I initialize my NSCache as follows:
_cellCache = [[NSCache alloc] init];
[_cellCache setDelegate:self];
[_cellCache setEvictsObjectsWithDiscardedContent:NO]; // never evict cells
The objects held in the cache implement the NSDiscardableContent protocol. Now my problem is the NSCache class does not seem to be working correctly in a couple of instances.
1) First the smaller issue. NSCache's setCountLimit: states that:
Setting the count limit to a number less than or equal to 0 will have no effect on the maximum size of the cache.
Can someone shed some light on what this means? I.e., that the NSCache is maximal, and will issue discardContentIfPossible messages only when memory is required elsewhere? Or that the NSCache is minimal, and it will issue discardContentIfPossible messages immediately?
The former makes more sense, but testing seems to indicate that the later is what is happening. If I log calls to the discardContentIfPossible method in my cached object, I see that it is being called almost immediately -- after only a 2-3 dozen items have been added to the cache (each less than 0.5 MB).
Okay. So I try then to set a large count limit -- way more than I will ever need -- by adding the following line:
[_cellCache setCountLimit:10000000];
Then the discardContentIfPossible messages are no longer sent almost immediately. I have to load a lot more content into the cache and use it for a while before these message start occurring which makes more sense.
So what is the intended behaviour here?
2) The larger issue. The documentation states:
By default, NSDiscardableContent objects in the cache are automatically removed from the cache if their content is discarded, although this automatic removal policy can be changed. If an NSDiscardableContent object is put into the cache, the cache calls discardContentIfPossible on it upon its removal.
So I set the eviction policy to NO (as above) so objects are never evicted. Instead, when discardContentIfPossible is called, I clear and release the internal data of the cached object according to special criteria. That is, I may decide not to actually clear and discard the data under certain circumstances (for example if the item has been very recently used). In such a scenario, I simply return from the discardContentIfPossible method not having discarded anything. The idea is that some other object that isn't recently used will get the message at some point, and it can discard it's content instead.
Now, interestingly, all seems to work great for a while of heavy use. Loading lots of content, placing and accessing objects in the cache, etc. After some time though, when I try to access an arbitrary object in the NSCache it's literally not there! Somehow it appears it has been removed -- even though I specifically set the eviction policy to NO.
Okay. So implementing the delegate method cache:willEvictObject: shows it never gets called. Which means the object is not actually getting evicted. But it's mysteriously disappearing from the NSCache since it can't be found on future lookups -- or somehow the key it was associated with is is no longer mapped to the original object. But how can that happen?
BTW, the key object that I'm associating with my value objects (CALayer's) is an NSURL container/wrapper, since I can't use NSURL's directly as NSCache doesn't copy keys -- only retains them, and a later NSURL key used for lookup might not be the exact original object (only the same URL string) that was initially used to load the cache with that object. Whereas with an NSURL wrapper/container, I can ensure it is the exact original key object that was used to add the original value object to the NSCache.
Can anyone shed some light on this?
Your comment "never evict cells" is not what -setEvictsObjectsWithDiscardedContent: says it does. It says it won't evict cells just because their content was discarded. That's not the same as saying it will never evict cells. You can still run past the maximum size or count and they can still be evicted. The advantage of discardable content is that you may be able to discard some of your content without removing yourself from the cache. You would then rebuild the discarded content on demand, but might not have to rebuild the entire object. In some cases this can be a win.
That brings us to your first question. Yes, NSCache starts evicting when it hits the maximum size. That's why it's called the "maximum size." You don't indicate whether this is Mac or iPhone, but in both cases you don't want the cache to grow until memory is exhausted. That's bad on a number of fronts. On Mac, you're going to start swapping heavily long before memory is exhausted. In iOS, you don't want to start sending memory warnings to every other process just because one process got crazy with its cache. In either case, a cache that is too large provides poor performance. So objects put into an NSCache should always expect to be evicted at any time.

Should I avoid creating lots of variables?

I have two possibilities:
1) Store an object in a variable and use that variable in my code. But this uses memory to store the object right?
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
BOOL bool1 = [userDefaults boolForKey: key1];
BOOL bool2 = [userDefaults boolForKey: key2];
2) Don't store it in a variable and create it from scratch when I needed.
BOOL bool1 = [[NSUserDefaults standardUserDefaults] boolForKey: key1];
BOOL bool2 = [[NSUserDefaults standardUserDefaults] boolForKey: key2];
What would be recommended in this case? If there's a difference between objects then how would I know which one to use?
As far as the particular example you showed in your question is concerned, there's no practical difference between those two ways of getting the NSUserDefaults. Unless you are dealing with large data objects (like UIImages) you should be concentrating on the clarity and readability of your code. If it turns out that you have a memory issue during your testing, come back and find ways to use less memory later.
You haven't been very specific, as to what kind of data. But irregardless, the things variables point at are what consume memory, not the variables themselves.
As for the rest of your question, if you leave a comment explaining your application centered around this question, as in what part your system is doing at this moment, I'll edit this to reflect that answer.
EDIT: Someone else answered and was accepted before I could update my answer. See the accepted answer.
If the operation executed is a very performance heavy method, it might be a performance loss to redo it every time, when you simply can save the information.
I feel you have to choices
If you have a problem with allocated memory, redo it.
If memory problem isn't an important factor and the executed operation isn't that heave that you have a performance loss, redo it.
I believe that doing iPhone-apps, the memory is not that super critical (might be now with multitask however), it's more critical to have the workload in mind, i.e. try not to redo same work. But as said, it all depends on the situation.
The wording of your question suggests you may not fully understand C pointers. You're not "storing an object in a variable." The userDefaults variable in your first example is just a C pointer. The +standardUserDefaults method returns a pointer to the global user defaults object, which you assigned to userDefaults. In your second example, you retrieved a pointer to the object twice, once for each line.
The only real difference between the two is that in the second example, you're doing a redundant message-send to retrieve the object reference again. Your examples aren't performance-critical, but there are cases where you might want to cache a reference to an object to avoid sending redundant messages, such as in a loop. It all depends on the performance of your program.
If you're not comfortable with C pointers, definitely read up on them. It should clear up some things for you.
You are not using any more of your program's memory in the first example. The NSUserDefaults object will be stored on the heap either way, and the storage for the variable itself will either be a) a register, or b) on the stack. Registers don't take up any of your app's real memory, and the memory for the stack is already reserved.
Also, pointers take up a very small amount of memory — 4 bytes on 32-bit and 8 bytes on 64-bit platforms. A quarter of a million of them would take less than 1 MB. Individual scalars like this are the very last place you should be looking for memory savings.