I want to verify if I'm in the last cell of the UITable.
Should I use a NSInteger property in my controller and store the value of the count of the data (it's an NSMutableArray) that populates the table?
I'm asking because I don't know if count iterates over all objects of the array to count or if it only gets me a value of a count property.
What would be better ?
store the value: myCount = [myArray count]
use directly: [myArray count]
store numberOfRows: myCount = numberOfRowsInSection:[indexPath 0]
use numberOfRows directly: numberOfRowsInSection:[indexPath 0]
Use the method directly. It is almost certainly efficient enough. If you are having serious performance problems, use shark and instruments to discover why. If you see most of your time is being spent in calls to count, find a way to optimize that, like you suggested here. But don't optimize prematurely like this--you are just wasting your time even thinking about it.
Because the state of the table view can change at any time, depending on what the iPhone OS is doing at any given time, you will almost always want to pull state data from your model (from the array, in this case).
Related
Im using a NSFetchedResultsController for my UITableView which displays a bunch of events im storing in core data.
What i am trying to do is group the table by relative date (ie Today, Tomorrow, This Week, etc..). Each event has a start date and i tried creating a transient property in the event entity called sectionIdentifier which converts the date into a relative date as mentioned above like so:
- (NSString*)sectionIdentifier
{
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveSectionIdentifier];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp)
{
tmp = [Utility formattedDateRelativeToNow:self.startTime];
[self setPrimitiveSectionIdentifier:tmp];
}
return tmp;
}
The problem is that it obviously only does this once and doesn't update itself unless the date is changed which i dont really expect. I have thought of overriding the getStartDate accessor to update the sectionIdentifier although this seems a little heavy handed and inefficient as it would perform this update every time i access that property
Any ideas?
The best method for doing this is rather counterintuitive. Instead of changing the fetched results controller or anything in Core Data, you extend NSDate with a category to have a keyname/method that returns a value based on the appropriate date calculation. You then include the keyname/method in the fetched results controller sectionNameKeyPath.
See this previous answer for an example of how to extend NSDate with keyname/methods like today, yesterday, tomorrow etc.
To use this, you would just take on the method to a date attribute of the entity the fetched results controller fetches like so:
NSFetchedResultsController *frc=[[NSFetchedResultsController alloc] initWithFetchRequest:aFetch
managedObjectContext:aMoc
sectionNameKeyPath:#"startdate.yesterday"
cacheName:nil];
... and the sections will appear automatically.
I think you will need to update you section headers as frequently as the smallest duration for which you are displaying a section (which seems to be one day). I would have proceeded in the following fashion-
1) Save the current time-stamp in applicationDidFinishLaunching using NSUserDefaults, etc.
2) Next time the application is launched, determine the difference between the current time-stamp & the one saved.
3) If the difference is more than your smallest duration (one day), regenerate your table, including section headers, as Today would have become Yesterday & Yesterday may have become last week.
HTH,
Akshay
I found that the common way for scrolling forward through a result set is using sqlite3_step():
while (sqlite3_step(statement) == SQLITE_ROW) {
// do something with the row
}
Is there a way to scroll backwards the result set, like going one step back or accessing a previous row using its ROWID?
Somewhere I have read that you can retrieve each record of the result set by its ROWID, but I can't figure out how.
If there is no such way for retrieving arbitrary rows in the result set, I will like to know whether it would be a bad practice or just a technologic limitation the reason for it.
So I was going through the functions in the sqlite3 framework since I couldn't find any documentation online and found this:
sqlite3_reset(statement)
I didn't test, but since it takes a sqlite3_stmt just like sqlite3_step() does I assume it rewinds the result set back to the beginning.
I have a set of data inside a dictionary and inside
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
method I want to fetch data from this dictionary. How can we fetch objects for each indexPath.row.
One way, I could think of is getting keys array and then fetch the object from this array based on indexPath.row and then use this key to fetch the dictionary object.
Please suggest.
dictionary is not an ordered collection like array, so the method you've suggested is probably the way to go... Depends on how do you want the data to be presented (sorted by keys/values).
It's not a good practice, because the order of the keys of a dictionary is not defined.
If your data must be a dictionary, then you must provide an explicit information that maps keys of the dictionary and the index path of the table.
For example, you can have another array in which the keys of the dictionary in sorted in the same order by which you would see your objects in the table.
How are your items sorted in the table? Is the order important? There isn't really an easy answer to this question. Are you sure you need to keep a dictionary of your data? The way I see, you have the following options:
call [dictionary allValues] to get an array of all values. You can then sort it the way you want and return the item at index indexPath.row. The draw back is you need to sort the array for each row which is not ideal if you have a lot of items. It might make your scrolling choppy.
keep a copy of a sorted array version of your dictionary and make sure to update it any time your dictionary is updated. This is better suited for a large data set that does not change often. You could wrap both your dictionary and array in a class that will take care of keeping the two in sync.
You'll need to weigh the drawbacks depending on what your data is like.
I have a Core Data database of about 500 objects. These objects are 'cards' that will be viewed and modified by the user. When a user modifies the card, an attribute called 'groupNumber' will change.
The order of these cards in each group is very important and is determined by the user. I load the database objects into an array. When a user makes a change, I save the order of the array into a plist using the 'title' attribute.
My problem comes when the app is relaunched. I need to load the group array in the order it was saved in. But when I use the plist to do a fetch request, it is painfully slow.
The slow code is:
// get array from plist sorted by 'title'
NSMutableArray *group1Temp = [plistData objectForKey:#"group1ArrayData"];
for (int i = 0; i < [group1Temp count]; i++) {
// set predicate to 'title' attribute
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title == %#", [group1Temp objectAtIndex:i]];
// load filtered database object into temp array
NSArray *temp = [self.vocabDeckArray filteredArrayUsingPredicate:predicate];
[self.group1Array addObjectsFromArray:temp];
}
When this executes 500 times, it is just too slow. If I could save an NSArray of the database objects themselves into a plist, then I wouldn't need to do a predicate search, but it seems I can't do that.
I'm thinking my entire approach to this has been wrong. How can I save/load the order of an array of database objects in a faster way? Is loading database objects into an array itself bad practice?
Thanks for any help!
Traversing the entire vocabDeckArray and filtering it once for each object in group1Temp is very inefficient. There are any number of ways you could rebuild this sorted data set in less than O(n^2) time.
One easy option might be to store a dictionary with the object's titles as keys and their position in the array as values. That way you can construct an array of known length and put every object in vocabDeckArray into the correct position in a single pass (get first object from vocabDeckArray, lookup where in belongs in group1Array from the dictionary, insert into group1Array, move on to next object). That's still not particularly fast but it seems like a minimal change to your current behavior.
In addition consider the number of method calls within your loop. self.vocabDeckArray and self.group1Array are method calls which you make on every iteration of your loop even though they always return the same objects. Keeping local variables which reference those objects instead would save you the overhead of 2 method calls on every iteration.
What's the difference?
In my context, I need to be able to dynamically add to and remove objects. The user clicks on rows of a table that check on and off and thus add or remove the referenced object from the list.
A wild guess is that array has indexed items while set has no indexes?
An NSSet/NSMutableSet doesn't keep items in any particular order. An NSArray/NSMutableArray does store the items in a particular order. If you're building a table view, you should definitely use an array as your data source of choice.
Also, NSMutableSet makes sure that all objects are unique.
NSMutableArray goes well with UITableView since elements have index, so you can return [array count] to get number of table rows, or [array objectAtIndex:rowNumber] to easily associate element with row.
Also, according to the documentation, testing for object membership is faster with NSSets.
You can use sets as an alternative to arrays when the order of
elements isn’t important and performance in testing whether an object
is contained in the set is a consideration—while arrays are ordered,
testing for membership is slower than with sets.
Well there are 3 main differences. 1) Sets are unordered 2)Don't have an index & 3)Cannot have duplicate values where Arrays are ordered, indexed & can store duplicate values.