read/write an NSCountedSet to plist - ios

I am developing a game app where i am using an NSCountedSet as my character inventory where the inventory dynamically changes from view to view.
In other words:
the user can buy items from view 1 and add to the inventory, then the user switches to view 2 and uses some items and those should be removed from the inventory, and so on..
My questions are:
1.How can I write and read a NSCounted set efficiently to a plist?
2.is the best approach to write the data to disk as view 1 closes and the reread the data as view 2 opens? or is there a way i can read the data once when the app launches, make all the changes and then save the data back when the app is terminating?
The data consists of strings and numbers only and is small in ammount.
THe following are snippets from my code:
- (void) initInventory
{
//initialize the inventory with some string objects
[Inventory addObject:#"x"];
[Inventory addObject:#"y"];
[Inventory addObject:#"z"];
}
- (void) addItemToInvetory:(NSString*)ItemName
{
//add object passed in method to the inventory
[Inventory addObject:ItemName];
}
- (void) removeItemFromInventory:(NSString*)ItemName
{
//add object passed in method to the inventory
[Inventory removeObject:ItemName];
}

1.How can I write and read a NSCounted set efficiently to a plist? ...The data consists of strings and numbers only and is small in amount.
You can just record it using an array of (alternating) strings and numbers. The number represents the count of the string object. For a small set, you should not need to worry about the performance of the operation.
2.is the best approach to write the data to disk as view 1 closes and the reread the data as view 2 opens? or is there a way i can read the data once when the app launches, make all the changes and then save the data back when the app is terminating?
You can pass it (the model) from one view controller to the next, and just share the same model instance in many cases. Whether it makes sense to dispose or not depends on whether or not you need a reference, and how often that information is needed. So best practice depends on the memory and your ability to ensure the data is correct. For example, you may opt to share in order to avoid unnecessary I/O, and to keep the data synchronized, but you should avoiding holding thousands of objects if you don't need them anytime soon.
If your data were not small, you should consider something like CoreData instead (3 values is extra-tiny).

Related

Advice - How to model detail list

I am working with a single TableViewController
The object that is displayed in TableViewController is Task
struct Task{
var type: String
var children = [Task]()
}
I store the base Task objects in a TaskStore. The TaskStore has a member pivot, which I use to track the current element in TaskStore. By Default, pivot is -1, and the TableViewController lists all objects in TaskStore. When a user clicks on a row in the TableViewController, I update pivot with the selected row, and display the Task objects in TaskStore.sharedInstance.get(pivot).children. The back button sets pivot back to -1 and displays the base TaskStore.
I am worried that this is overly complex. It works without a real problem, but I do need to add an exception whenever Pivot is not -1.
Is there a more suitable way to display the children of a Task object in TaskStore without the need to create a new TableViewController?
Thanks for any advice
The reason you are unhappy is that you are doing a lot of complicated work in your cellForRowAtIndexPath to pick up the right data. It has to interpret the pivot and dive appropriately into the task store. I suggest you forget all that. Knowing how to dive into the task store is not the business of the table view's data source!
What I would do, therefore, is separate the overall hierarchical model from the table's actual data source. The data source, at any given moment, should just consist of an array of Tasks, a [Task].
The problem then devolves into maintaining that array. When the user taps a row, you can replace that array of Tasks with whatever you like and call reloadData. In this way you maintain a direct correspondence between the table rows and tasks array. cellForRowAtIndexPath just asks for the info from that row of the Tasks array and it's done. Simple and clear.
Meanwhile, the work of knowing about your hierarchy all happens in the TaskStore, which is not, itself, the data source for the table. You might also need some sort of pointer into the TaskStore saying what the currently displayed parent is, but again, that bookkeeping will happen outside the array and outside the knowledge of your cellForRowAtIndexPath.
You thus end up with encapsulation of functionality. The task store understands its internal hierarchy and how to fetch a desired set of tasks. The table view knows how to grab its rows using a simple one-to-one correspondence with the current tasks list.

Parse.com data caching and synchronization

What is the best strategy to synchronize Parse objects across the application?
Take Twitter as an example, they have many Tweet objects, same tweet object can be shown on multiple places, say viewController1 and viewController2, so it is not efficient for both of them to hold deep copies of the same parse object.
When I increase the likeCount of Tweet_168 in viewController2, how should I update the likeCount of Tweet_168 in viewController1?
I created a singleton container class (TweetContainer) so every Parse request goes through this and this checks if the incoming objectIds are already in the container,
A) if it is, it updates the previous object's fields and dumps the new object. (to keep single deep copy of a parse object.)
B) if it is not, it adds the new object.
(This process is fast as I'm using hashmaps)
This container holds deep copies to those objects, and gives shallow copies to viewControllers, thus editing a Tweet in a viewController will result in its update on all viewControllers!
Taking one step further, let's say Tweet objects have pointers to Author objects. When an Author object is updated, I want all of them to be updated (say image change). I can create a new AuthorContainer with the same strategy and give shallow copies to Tweet objects in TweetContainer.
I could, in an ideal world, propagate every update to cloud and refresh every object before showing to user over the cloud, but that's not feasible neither bandwidth nor latency-wise

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.

Good algorithm for reloading a batch of UICollectionView or UITableView changes?

Setup: I have a UICollectionView or UITableView that’s backed by a simple array data source. I keep a copy of that data source in the controller.
Now, I get a notification from the system that there’s new data available. I get a new array where items may have been added, removed and changed positions.
So now I have two data objects:
previous array that's in sync with what the UI is currently showing
new array where items have been added, removed, moved
To get the UI in sync with the new array, I need to generate a bunch of UI calls. In case of UICollectionView, those are
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
And there’s a similar set of methods for UITableView.
I specifically don’t want to reload the whole table because that’s more expensive than just working with a few items.
So, the question is: given the previous and new data source array, how do I generate the correct set of UI calls, and when do I "swap out" my old data source for the new?
I'd see this as largely equivalent to the diff / patch problem, where the aim is to find the minimal number of changes between one text file and another and then to apply those changes. In that case, the implementation defines the operations:
add or insert
delete
change
... but not move. The reason for omitting move isn't immediately obvious to me but I'd strongly suspect that including move would require a very costly computation to find the optimal movement.
So, if we restrict the operations to those listed above, the Hunt-McIlroy algorithm described in An Algorithm for Differential File Comparison, or one of its descendents, would find a close-to-optimal set of changes.
The difference between your problem and the classic diff / patch is that you have a two-dimensional table, while diff / patch deals with a one-dimensional set of items (lines of text). The best way to convert the 2-D problem into the 1-D problem would depend on the particular characteristics of the changes that tend to be made in your data table.
For example, if the table is n rows by m columns and changes tend to be grouped in rows or rows are inserted or deleted as a whole, then you would likely be best to consider the table as if it was a text file and do the diff line by line. Alternatively, if changes tend to be grouped in columns or columns are inserted or deleted, you could do the diff column by column. If the alterations include inserting or deleting individual cells (which result in the subsequent cells shifting right or left as a result), you could treat the table as if each cell in the table was on a separate line of the text file, linearising the table in either row-first or column-first order.
Without knowing the details of your problem in this respect, however, my inclination would be to avoid premature optimisation. Thus, I'd tend to start by implementing the Hunt-McIlroy algorithm row-by-row if m < n, or column-by-column if n < m, then profile the application in use for a while before deciding that either a more sophisticated version of the algorithm or an alternative mapping of your problem to the Hunt-McIlroy solution was warranted.
A good discussion of a variety of diff algorithms can be found here on stackoverflow.
I have worked on an app that deals with a very similar problem that i was able to solve. I was expecting a complicated solution but it was really simple. Here is how you can solve it:
1) Create a new array in which you will receive new items and call it let us say 'moreItems', also synthesise it of course.
#property (nonatomic, readonly) NSMutableArray *moreItems;
2) In viewDidLoad of the ViewController that is linked with your TableView or CollectionView, alloc/init this array:
moreItems = [[NSMutableArray alloc] init];
3) Now you need to add your existing array let us say it was "items" to the new array that you have just created named 'moreItems'. You can do this using 'addObjectsFromArray'
[moreItems addObjectsFromArray:[channel items]];
'channel items' contains the objects it has received earlier and it will add these items to the newly created array called 'moreItems'. I am assuming you are collecting data from some web service, so you can implement this step in connectionDidFinsihLoading.
4) Now change your data source of TableView/CollectionView and replace it with the new array that is 'moreItems'
5) Next issue is that you don't want to reload the whole tableview and want to deal with new items. To have this functionality, you need to persist items. You can use archiving or core data whatever you are comfortable with.
6) The items that are already fetched, you will have to persist them let us say with archiving. And show them in the tableview when the user opens the app while it grabs more items that are updated on the web service.So first show the persisted items immediately and then handle the new items.
7) You need to look for some unique object, in my case it was the 'link' as every item has a different link and i can sort and handle them on this basis. You also need to use
- (BOOL) isEqual:(id)object
to compare the links on the web service and the links that are already in the tableview. This step is necessary because then your app won't add the items with links that are already in the tableview.
8) If there is some date associated with each item, you can use that date to sort them and show the new ones on top by using 'sortUsingComparator'
9) Also you need to use "[[self tableView] insertRowsAtIndexPath:rows withRowAnimation:UITableViewRowAnimationTop" to have that effect that shows user the new items have been added on top of the earlier ones.
Hope this helps.
I specifically don’t want to reload the whole table because that’s
more expensive than just working with a few items.
Are you sure this is true? Unless you have pathological case where every single cell is visible on your screen and there are hundreds of them, I find it hard to believe how will the "efficient" way be any faster than the "brute force" way.
Calling reloadData will only ask your delegate to update those cells that will end up visible on screen, it will not cause the entire table view to recreate every single of its cells.
If you're implementing some methods that change the complexity of calculating dimensions (like tableView:heightForRowAtIndexPath) you won't get any benefit using "efficient" way since the table view will have to recalculate all its heights anyway.
I'm not sure if solutions you are looking for will fix the problem, but let's just hope that I'm terribly wrong.
If your data source already has diff information, UITableView reloadRowsAtIndexPaths:withRowAnimation: will make it less-performance intensive:

Reading from SQLite in Xcode iOS SDK

I have program with a table in SQLite with about 100 rows and 6 colunms of text (not more than hundred of characters). Each time click a button, the program will display in the view a contents of a row in the table.
My question is that should I copy the content of whole table into an array and then reading from array each time user clicks button or I access the table in database each time the user click the button? Which one is more efficient?
It all depends, but retrieving from the database as you need data (rather than storing the whole thing in an array) would generally be most efficient use of memory, which is a pretty precious resource. My most extravagant use of memory would be to store an array of table unique identifiers (i.e. a list of primary keys, returned in the correct order), that way I'm not scanning through the database every time, but my unique identifiers are always numeric, so it doesn't use up too much memory. So, for something of this size, I generally:
open the database;
load an array of solely the table's unique identifiers (which were returned in the right order for my tableview using the SQL ORDER BY clause);
as table cells are built, I'll go back to the database and get the few fields I need for that one row corresponding to the unique identifier that I've kept track of for that one table row;
when I go to the details view, I'll again get that one row from the database; and
when I'm all done, I'll close the database
This yields good performance while not imposing too much of a drain on memory. If the table was different (much larger or much smaller) I might suggest different approaches, but this seems reasonable to me given your description of your data.
for me - it was much easier to re-read the database for each view load, and drop the contents when done.
the overhead of keeping the contents in memory was just too much to offset the quick read of a small dataset.
100 row and 6 columns is not a lot of data. iOS is capable of handling larger data than that and very efficiently. So don't worry about creating a new array. Reading from the database should work just fine.

Resources