How to use NSCache appropriately in iOS application? - ios

I am building an application and want to use NSCache to store data for caching.
There will be approx 5 API for which I need to cache data. Should I user NSCache? I did research for NSCache but I have some doubts regarding this.
I did go through below link.
https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemory/Articles/CachingandPurgeableMemory.html
I found some interesting things there.
NSCache provides two other useful "limit" features: limiting the number of cached elements and limiting the total cost of all elements in the cache. To limit the number of elements that the cache is allowed to have, call the method setCountLimit:. For example, if you try to add 11 items to a cache whose countLimit is set to 10, the cache could automatically discard one of the elements.
If we set limit to 10 and if we try to add 11th item then which particular item it will discard that is 1st one or 10th one? Or it can be a random item.
If I want to store 5 different NSCache object for all APIs then how can I do it?
I should make 5 different NSCache objects or I should store 5 different dictionary in single NSCache object?
Please let me know the best way to deal with it.

NSCache works basically as an NSMutableDictionary, the main difference is that even if is mutable is thread safe (usually mutable NSFoundation objects are not thread safe). So you get and set objects using keys.
Yes, the documentation is not clear, but I remember (and it makes sense) that is written that you should always detect if an object is cache and if not reload it from your primary source or manage that particular case. So it is not very important which one is removed, just make sure that at some point an object can not be there anymore and you are managing that situation.
I usually create different caches based on different context, but most of the time one would suffice.
I have few advices:
Your answer is tagged as Swift, thus pay attention that NSCache (in swift 2, don't know in 3) works only with objects and not struct or enumerations (value types unless thay can be bridged).
Remember that http protocol has its own cache system and communication, do not reinvent the wheel

Related

Does core data have FIFO?

I would like to ask is there any FIFO examples for swift coredata?
For example, I only allow user to stores 50 history. So when a user store another history, no. 51 will be entered, and no. 1 in core data will be deleted automatically.
Thanks!
You'll have to do this yourself. Managed objects aren't created or deleted automatically in most cases, so if you want to limit the number of instances of an entity, you need to write code to do that.
You'll probably need to add an attribute to your entity to keep track of the order, so that your code would know which was the first, second, etc, and work out which instance(s) to delete. That could be an integer index, or a creation date, or maybe something else.

Linked lists in practice

I have some questions about the ideas proposed in this video.
The speaker shows an array that holds values and pointers, and he also shows a separate "free" linked list, that is updated whenever an item is added/removed.
Why are these used? Doesn't using an array / limiting yourself to a set of free nodes defeat the purpose of a linked list?
Isn't one of the perk of using a linked list the ability to traverse fragmented data?
Why use these free nodes, when you can dynamically allocate storage?
The proposed structure, to me, doesn't seem dynamic at all, and is in fact a convoluted and inefficient array.
The approach you mention makes sense in certain use cases. For example if the common case is that the array is 90% full and most of the time is spent iterating over it, you can very quickly loop over an array and just skip the few empty items. This can be much, much faster than "pointer chasing" which plain linked lists use, because the CPU's hardware prefetcher can predict which memory you will need in advance.
And compared with a plain array and no free list, it has the advantage of O(1) allocation of an element into an empty slot.

iOS 7 - UITableViewController big data source - strategy advice?

Xcode: 5.0.2
iOS: 7.0.x
I have a secondary view that is conditionally shown when a user logs in to my app. This secondary view shows a list of items that a user must choose one of as a "default" value for the lifetime of their authenticated session. This secondary view is going to be seen only once by the large majority of my users.
The list of items are returned in JSON from a web service and can be anywhere from 1 item to 1000 items. If one is returned, the secondary view won't even show.
The json will be structured with two elements again each item, and id and an itemName. I've estimated a few hundred kb download for a worst case scenario - and its a one time download. Perhaps a searchable API rather than a data-dump would be better practice?
Once the results are return they will be processed into two NSArray. An NSArray of NSDictionary for me to retrieve an id once selected and an NSArray of NSString containing itemName - used for populating UITableView and performing the keyword search against;
For retrieving ID reference:
[ { id: 0, itemName: "one" }, { id: 1 itemName: "two" } ]
For populating the UITableView data source
[ "one", "two" ]
Now I need this data in my UITableView. As this is a one-time operation (changeable later, but the users typically will not be changing this regularly) I was planning on adding the entire array into the UITableView.
Typically, what is the max size that you should put into the table view? Will this cause me some serious memory issues? How will the keyword search fair when searching against 100's - 1000.
I'm also looking at perhaps updating the UI to follow that very closely of the Contacts app (UILocalizedIndexedCollation?) so again it will have an impact on this.
Thanks,
Having a JSON, a corresponding Foundation representation will consume roughly 5 times more RAM than the length in bytes.
So, for example if your JSON is 300 Kbyte (UTF-8), then NSJSONSerialization will create a Foundation hierarchy which may consume 1.5 MByte RAM.
This amount fits nicely into RAM.
If, and only if you don't want to persist the JSON representation or a corresponding Model, and don't want to utilize Core Data for other reasons (e.g. editing, undo, rollbacks, etc.), I would strongly argument against using Core Data: since your data is still "small" in the worst case, there's no need to utilize Core Data in the assumption it would safe you memory.
Core Data isn't cheap either, memory wise. In your case, using Core Data would actually cause your App to consume much more RAM, because of SQLite's buffers and caches, Core Data buffers and caches and internal structures. In practice and given your scenario with 1000 objects, a Core Data /SQLite approach would consume roughly 4 Mbyte. This is about 3 times more than your JSON representation. On the other hand, Core Data may not consume much more RAM when your number of elements increase tremendously and when there is also memory pressure.
I will also strongly recommended to you CoreData approach with UIFetchedResultController. This is solution crafted for your problem. While you're downloading the data from the web, FetchedResultController will display empty table view, and when download is complete or "enough" you might display the data without blocking user interface.
What's more, if you want to display a huge database at once, the controller manage to fetched more data "by itself" when user scroll down.
Of course UILocalizedIndexedCollation is also supported here.

About usage framework coreData

What is the best way to use CoreData:
1 - The first time I load all items from the store in the array, in the future, when I have to get one item (or more,meet certain properties), I just do filtering array (filteredArrayUsingPredicate)
OR
2 - Every time when I need to get the items (one item or several or all items) I load those games all from the repository?
When I am asking what is the best way, I actually mean what is the most efficient and most common way (quickest response time)?
Use MagicalRecord library. It has convienience methods like:
MR_findAll
MR_findFirst
MR_findFirstByAttribute and many, many others.
Don't use data arrays, and don't use 3rd party libraries.
Rather, read up on NSFetchedResultsController and discover how simple and scalable core data can be.

What's the effect of cache to NSFetchedResultsController

I read the doc of course, but I don't quite get the meaning of "setting up any sections and ordering the contents".
Don't these kinds of information come form data base?
Does it mean NSFetchedResultsController needs some other kinds of indices itself beside data base indices?
What's really going on when NSFetchedResultsController is setting up a cache?
Is cache useful for static data only? If my data update frequently, should I use cache or not?
How can I profile the performance of cache? I tried cache, but couldn't see any performance improvement. I timed -performFetch: but saw a time increase from 0.018s(without cache) to 0.023s(with cache). I also timed -objectAtIndexPath: and only a time decrease from 0.000030(without cache) to 0.000029(with catch).
In other words, I want to know when cache does(or does not) improves performance and why.
As #Marcus pointed below, "500 Entries is tiny. Core Data can handle that without a human noticeable lag. Caching is used when you have tens of thousands of records." So I think there are few apps that would benefit from using cache.
The cache for the NSFetchedResultsController is a kind of short cut. It is a cache of the last results from the NSFetchRequest. It is not the entire data but enough data for the NSFetchedResultsController to display its results quickly; very quickly.
It is a "copy" of the data from the database that is serialized to disk in a format that is easily consumed by the NSFetchedResultsController on its next instantiation.
To look at it another way, it is the last results flash frozen to disk.
From the documentation of NSFetchedResultsController:
Where possible, a controller uses a cache to avoid the need to repeat work performed in setting up any sections and ordering the contents
To take advantage of the cache you should use sectioning or ordering of your data.
So if in initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName: you set the sectionNameKeyPath to nil you probably won't notice any performance gain.
From the documentation
The Cache Where possible, a controller uses a cache to avoid the need
to repeat work performed in setting up any sections and ordering the
contents. The cache is maintained across launches of your application.
When you initialize an instance of NSFetchedResultsController, you
typically specify a cache name. (If you do not specify a cache name,
the controller does not cache data.) When you create a controller, it
looks for an existing cache with the given name:
If the controller can’t find an appropriate cache, it calculates the
required sections and the order of objects within sections. It then
writes this information to disk.
If it finds a cache with the same name, the controller tests the cache
to determine whether its contents are still valid. The controller
compares the current entity name, entity version hash, sort
descriptors, and section key-path with those stored in the cache, as
well as the modification date of the cached information file and the
persistent store file.
If the cache is consistent with the current information, the
controller reuses the previously-computed information.
If the cache is not consistent with the current information, then the
required information is recomputed, and the cache updated.
Any time the section and ordering information change, the cache is
updated.
If you have multiple fetched results controllers with different
configurations (different sort descriptors and so on), you must give
each a different cache name.
You can purge a cache using deleteCache(withName:).

Resources