problem with saving data at coredata? - objective-c

In my application there is searchBar. when we input a text, it will do functionGrab (grab data from internet and save it to coredata), example :
if we input "Hallo"
if([[dict objectForKey:#"Category"] isNotEmpty] && [[[dict objectForKey:#"Category"] objectAtIndex:0] class]!=[NSNull class]){
NSMutableArray * DownloadedTags =[dict objectForKey:#"Category"];
NSMutableSet * TagsReturn=[NSMutableSet set];
for(int i=0;i<[DownloadedTags count];i++){
NSString * Value=[DownloadedTags objectAtIndex:i];
Tag * thisTag= (Tag*)[GrabClass getObjectWithStringOfValue:Value fromTable:#"Tag" withAttribut:#"Name"];
[TagsReturn addObject:thisTag];
}
NSMutableSet * manyManagedObjects = [BusinessToSave mutableSetValueForKey:#"Tags"];
[self removeDifferenceBetween2MutableManagedObjectSets:manyManagedObjects withDownloadedVersion:TagsReturn];
}
So each biz has many categories. WHat happen in multi threaded application is one thread put category. The other thread also put the same category before committing.
So, [GrabClass getObjectWithStringOfValue:Value fromTable:#"Tag" withAttribut:#"Name"]; gives a new object even though some other thread already created the same object without knowing it.
If I synchronized the whole thing that the code would run serially and that'll be slow.
functionGrab:"H"
functionGrab:"Ha"
functionGrab:"Hal"
functionGrab:"Hall"
functionGrab:"Hallo"
something like,it do that functionGrab 5 times
I want to make functionGrab at background, but the problem is when I do that function without synchronized it will save more than one of data, so the result is there are 5 hallo words in my coredata, but if I do that with synchronized, it spent so much time, so slow..
is there any way to help my problem?

I do not recommended having more than one thread "creating" the same types of data for the exact reason you are running into.
I would suggest you queue all of your "creates" into a single thread and a single NSManagedObjectContext to avoid merge or duplication issues.
The other option would be to make the app Lion only and use the parent/child NSManagedObjectContext design and then your children will be more "aware" of each other.

Related

coding efficiency vs execution efficiency

So I have these two methods:
-(void)importEvents:(NSArray*)allEvents {
NSMutableDictionary *subjectAssociation = [[NSMutableDictionary alloc] init];
for (id thisEvent in allEvents) {
if (classHour.SubjectShort && classHour.Subject) {
[subjectAssociation setObject: classHour.Subject forKey:classHour.SubjectShort];
}
}
[self storeSubjects:subjectAssociation];
}
-(void)storeSubjects:(NSMutableDictionary*)subjects {
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *subjectsList = [documentsDir stringByAppendingPathComponent:#"Subjects.plist"];
[subjects writeToFile:subjectsList atomically:YES];
}
The first loops through an array of let's say 100 items, and builds a NSMutableDictionary of about 10 unique key/value pairs.
The second method writes this dictionary to a file for reference elsewhere in my app.
The first method is called quite often, and so is the second. However, I know, that once the dictionary is built and saved, its contents won't ever change, no matter how often I call these methods, since the number of possible values is just limited.
Question: given the fact that the second method essentially needs to be executed only once, should I add some lines that check if the file already exists, essentially adding code that needs to be executed, or can I just leave it as is, overwriting an existing file over and over again?
Should I care? I should add that I don't seem to suffer from any performance issues, so this is more of a philosophical/hygienic question.
thanks
It depends.
You say
once the dictionary is built and saved, its contents won't ever change
until they do :-)
If your app is not suffering from any performance issues on this particular loop I wouldn't try to cache for the reason that unless you somehow remember that you have a once-only write on the file you are storing up a bug for later.
This could be mitigated by using an intention revealing name on the method. i.e
-(void)storeSubjectsOnceOnlyPerLaunch:(NSDictionary*)subjects
If I got my time back for tracing down bugs caused by caching, I would have several days back in my life.
Your solution is totally over engineered, and has tons of potential to go wrong. What if the users drive is full? Does this file get backed up? Does it need backing up / are you wasting the users time backing it up? Can this fail? Are you handling it? You are concentrating on the entering and storing of data, you should be focusing on accessing that data.
I'd have a readwrite property allEvents and a property eventAssociations, declared readonly in the interface, but readwrite in the implementation file.
The allEvents setter stores allEvents and sets _eventAssociations to nil.
The eventAssociations getter checks whether _eventAssociations is nil and recalculates it when needed. A simple and bullet-proof pattern.

NSMutableArray synchronization with priority

I have some NSMutableArray , that i am adding object to in another thread (async) , with :
#synchronized(array)
{
[array addObject:backImageView];
}
Than, there is another place where i remove objects from him, in the main thread,
//remove from array
for(UIImageView *image in array)
if( (image.tag< page-numPagesLoads || image.tag> page+numPagesLoads) && image.tag!=-11)
[array removeObject:image];
Question is, that i want to give a better priority to the place who adds the object to him, so no matter what, stop the use of this array , add new object, and if in the next time you try to remove object you dont interrupted by the "add object" , and remove .
How would you do that ?
The adding and removing objects from an array will happen quickly enough that there will be no discernible advantage to adding some complicated prioritization logic here. Usually you'd just adopt the simple and logical "first come, first serve" approach inherent in all locks/queues/etc.
FYI, I don't see you using #synchronized in your second example, but I assume you were using it there, too, but that it was omitted for the sake of brevity. When using locks to synchronize objects across multiple threads, you have to use those locks everywhere you use this object.
Personally, because (a) you're dealing with such a trivial and fast operation; and (b) you're dealing with an array of UI objects, I'd be inclined to suggest you cut the Gordian knot, and just dispatching all the manipulation of this array (and the corresponding UIKit objects) to the main queue, which simplifies your task.

NSFetchedResultsController missing some objects

I'm facing a strange issue where an NSFRC fetchedObjects array returning not all the objects it should. To give you some context, my application has several list view controllers, each of them having an NSFRC. I'm updating the list view within the delegate method controllerDidChangeContent. The problem I'm facing is the following: after storing an object in a background MOC and saving it, the controllerDidChangeContent is invoked but the object I just saved in the background thread doesn't show up in the NSFRC. Here is a piece of code that I'm using to check this:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
NSManagedObjectContext *context = controller.managedObjectContext;
NSError *error = nil;
NSArray *array = [context executeFetchRequest:controller.fetchRequest error:&error];
if (nil != array) {
NSUInteger count = MIN(controller.fetchedObjects.count, array.count);
for (NSUInteger index=0; index<count; index++) {
NSManagedObject *a = array[index];
NSManagedObject *b = controller.fetchedObjects[index];
// Here you will see that sometimes the objects don't match
NSLog(#"%d: %# <--> %#", index, [[a body] text], [[b body] text]);
}
}
}
I'm expecting the NSFRC fetchedObjects array to be identical to the array returned by a manual executeFetchRequest (I'm using the NSFRC fetchRequest to manually fetch the data). However, this is not the case. The manual executeFetchRequest returns more object than the NSFRC fetchedObjects. Does anyone know what's going on? I've turned the caching on the NSFRC off but the same behavior is reported.
Thanks!
=== Update ====
Some update on that issue. I think there is a bug in Core Data because I was able to see some inconsistent results from the NSFRC and moreover was able to fix the problem by a workaround involving "touching" the object in question. Here is a scenario that explains what is happening:
Imagine the following Core Data model where:
- There are Cat objects and Master objects.
- A Cat can have one or more Master.
- A Master can have one or more Cat.
- A first NSFRC (let's call it NSFRC_A) is created to fetch all the cats with master named "Master_A". The predicate is { ANY master.name == "Master_A" }.
- A second NSFRC (let's call it NSFRC_B) is created to fetch all the cats with master named "Master_B". The predicate is { ANY master.name == "Master_B" }.
- There is a main managed object context that is used in the UI thread only
- There is a background managed object context created for each background thread, using the same persistent store as the main managed object context.
A cat named "Cat_A" is created in the background and assigned to master "Master_A". After the background context is saved, the main context is updated appropriately. At this point, the NSFRC_A notifies its delegate that a change has occurred and correctly reports "Cat_A".
Later on, in a background thread, the same cat "Cat_A" is assigned master "Master_B". After the background context is saved, the main context is updated appropriately. At this point, the NSFRC_A notifies its delegate of that change and correctly reports "Cat_A". NSFRC_B also notifies its delegate of that change but doesn't report "Cat_A" (it is missing from its fetchedObjects). However, if I manually perform a fetch using the same fetchRequest as NSFRC_B, I can see "Cat_A" being returned. The weird thing is that the "Cat_A" instance being returned is marked as a fault which explains why NSFRC_B doesn't return the "Cat_A" because it doesn't see it in memory.
This is a bug because I can fix that behavior by simply logging the "Cat_A" relationship to master when the changes from the background thread are merged into the main context: the logging basically touches the object and forces it to be realized into memory.
The problem appears to be a limitation of the NSFRC. According to this thread on the Apple Forum (https://devforums.apple.com/message/765374): "The limitation being that a fetched results controller for entity A won't always catch an update to entity B that would cause the predicate to change.". To solve the issue, I had to dirty the object I'm looking for before it is being merged into the main thread: then the NSFRC detects that change.

Core data: executeFetchRequest vs performFetch

I want a thorough list regarding comparison between the two. Things I have known:
executeFetchRequest:
Message sent to MOC
Return an array of managed objects
Goal: fetch objects from persistent store to MOC
With table view: has nothing to do with table view
Frequency: often used in a loop, so could be called many many times
performFetch:
Message sent to FRC
After calling it, use fetchedObjects to return an array of managed objects
With table view: FRC is specifically for keeping managed objects and table view rows in sync, and use performFetch to initialize that process.
Frequency: often only once. Unless fetch request of FRC changes, no need to call performFetch a second time
Please correct me if I am wrong and append the list. Thank you.
About executeFetchRequest:
Message sent to MOC
Yes
Return an array of managed objects
Yes, but you can also change the type of results you want to retrieve. In NSFetchRequest you can set a different result type with:
- (void)setResultType:(NSFetchRequestResultType)type
where NSFetchRequestResultType can be of different types. Taken from Apple doc:
enum {
NSManagedObjectResultType = 0x00,
NSManagedObjectIDResultType = 0x01,
NSDictionaryResultType = 0x02
NSCountResultType = 0x04
};
typedef NSUInteger NSFetchRequestResultType;
Goal: fetch objects from persistent store to MOC
Yes, creating a NSFetchRequest and performing a request, it the same as creating a SELECT statement in SQL. If you also use a NSPredicate it's the same as using SELECT-WHERE statement.
With table view: has nothing to do with table view
Yes, but with retrieved data you can populate a table
Frequency: often used in a loop, so could be called many many times
It depends, on what you want to achieve. It could be within a loop or not. Executing the request within a loop could have impact on performance but I would not be worried on that. Under the hood Core Data maintains a sort of cache mechanism. Every time you perform a request, if data are not in the cache, Core Data executes a round trip on your store (e.g. sql file) and populate the cache with the objects it has retrieved. If you perform the same query, the round trip will not performed again due to the cache mechanism. Anyway, you could avoid to execute a request within the run loop, simply moving that request outside the loop.
About performFetch:
Message sent to FRC
Yes
After calling it, use fetchedObjects to return an array of managed
objects
Yes, but you can also retrieve an object with [_fetchedResultsController objectAtIndexPath:indexPath]; if you are populating a specific cell within a table.
Here I really suggest to read a nice tutorial on NSFetchedResultsController
With table view: FRC is specifically for keeping managed objects and
table view rows in sync, and use performFetch to initialize that
process.
Yes, a NSFetchedResultsController works in combination with a NSManagedObjectContext for you. Furthermore, it enables lazy loading of data. Suppose you have 1000 elements you retrieve and you want to display them in a UITableView. Setting a request for a NSFetchRequest like:
[fetchRequest setFetchBatchSize:20];
and using it with an instance of a NSFetchedResultsController, it allows to load 20 elements at first. Then when you scroll, other 20 elements are loaded, and so on. Without a NSFetchedResultsController you must implement this behavior manually. Refer to the tutorial I provided for further info.
Frequency: often only once. Unless fetch request of FRC changes, no
need to call performFetch a second time
It depends on what you want to achieve. Most of the time you could call it once.
Hope that helps.
Edit
You have to call performFetch explicitly. I like to create a property for NSFetchedResultsController in my header file (.h) like
#property (nonatomic, strong, readonly) NSFetchedResultsController* fetchedResultsController;
and synthesize it in your implementation file (.m) like
#synthesize fetchedResultsController = _fetchedResultsController;
Then always within the .m file override the getter to create an new instance of it:
- (NSFetchedResultsController*)fetchedResultsController
{
// it already exists, so return it
if(_fetchedResultsController) return _fetchedResultsController;
// else create it and return
_fetchedResultsController = // alloc-init here with complete setup
return _fetchedResultsController;
}
Once done, within your class (for example in viewDidLoad method) use it like
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
You are comparing the wrong elements. NSFetchedResultsController uses the NSManagedObjectContext to perform the fetch, and under proper configuration, monitors the changes in the managed object context to verify the status of the fetch properties it is monitoring, but the actual fetches are done by the context. On both cases, NSManagedObjectContext does the fetch. The difference being that, using the NSManagedObjectContext directly, you get an NSArray type of object (the actual runtime class is different than an array you get using [NSArray array]), while NSFetchedResultsController has a different purpose (have a collection of results and monitor changes to the records and entity on its fetch request). In other words, NSFetchedResultsController works using the context, but it works different than just a simple collection of objects.
One observation: you shouldn't be using executeFetchRequest inside a loop, especially calling it "many many times". Each fetch has its performance cost. You can call executeFetchRequest once, and do a loop to check the result.

How do I efficiently update a UITableView with Animation?

My iPad app features a UITableView populated from a feed. Like most RSS readers, it displays a list of links to blog posts in reverse chronological order, with their titles and a summary of each post. The feed updates frequently, and is quite large, around 500 posts. I'm using libxml2 push parsing to efficiently download and parse the feed in an NSOperation subclass, constructing entry objects and updating a database as I go. But then I need to update the UITableView with changes.
So far, the app has been updating the UITableView for every post parsed, as it is parsed. The parser performs a selector on the main thread to do this work. But this leads to some serious lag for a couple of seconds if a lot of cells need to be updated. I can mitigate this by running the table update on a background thread, but it seems that this is not a good idea. So now I'm trying to figure out how to update the table more efficiently on the main thread.
I could just call reloadData when all the posts have been parsed, but it's not very user friendly: there's no animation to indicate that anything has changed, just a flash and the new data is there. I'd much rather have it animate to show that new posts are added and old posts removed. Existing posts that are not removed from the feed should be pushed down the table by the new posts appearing at the top.
I know this is possible. Byline, to give one example, does a beautiful job. Each post is added or removed from the UITableView one-at-a-time with no gaps showing the table background. All without making the UI in the least bit unresponsive. How is that done??
My latest attempt is to update the table only after all the posts have been parsed (the parser is quite fast, so it's not much of a delay). It then loads the existing posts in an NSDictionary mapping their IDs to their indexes in the array used as the table data source. It then iterates over every object in the newly-parsed array of posts, adding NSIndexPath for each to arrays that are later passed to -insertRowsAtIndexPaths:withRowAnimation:, -deleteRowsAtIndexPaths:withRowAnimation:, and -reloadRowsAtIndexPaths:withRowAnimation: as appropriate to insert, remove, move, or update cells. For 500 posts, this takes around 4 seconds to update, with the UI completely unresponsive. That time is used almost exclusively for the UITableView animated updates; iterating over the two arrays of posts takes very little time.
I then modified it so that those are updated without animation, and I have separate arrays to insert/delete/reload with animation only for row positions corresponding to currently-visible rows. This is better, but gaps appear as posts are removed and new ones added.
Sorry this is so long-winded, but here's the upshot:
How can I update a UITableView, with new cells pushed on, others pushed off, and still others moved from one position to another, with up to 500 cells in the UITableView (6-8 are visible at one time), and each animation happening in sequence, all while the UI remains completely responsive?
This question actually has three answers. That is, there are three parts to this question:
How to keep the UI responsive
How to keep the updating fast
How to make table update animation smooth
UI Responsiveness
To solve the first problem, I now make sure that no more than one table-updating message can be delivered on each iteration of the main event loop. That prevents the main thread from locking up if the background thread is feeding it stuff to do faster than it can cope with it.
This is done thanks to example code sent to me by Byline author Milo Bird, which I then integrated into Dave Dribin's DDInvocationGrabber. This interface makes it super easy to queue a method to be invoked on the next available iteration of the main event loop:
[[(id)delegate queueOnMainThread]
parserParsedEntries:parsedEntries
inPortal:parsedPortal];
I quite like how easy it is to use this method. The parser now uses it to call all of the delegate methods, most of which update the UI. I've released this code on GitHub.
Performance
As for performance, I was originally updating one UITableView row at a time. This was effective, but somewhat inefficient. I went back and studied the XMLPerformance example, where I noticed that the parser was waiting until it had collected 10 items before dispatching to the main thread to update the table. This was key to keeping the performance up without making the UI lock up by updating all 500 rows at once. I played around with updating 1, 10, and all 500 rows in a single call, and updating 10 seemed to offer the best tradeoff between performance and UI lockup. five would probably work pretty well, too.
Animation
And finally, there's the animation. Watching the “Mastering Table Views” WWDC 2010 session, I realized that my use of the deleteRowsAtIndexPaths:withRowAnimation: and updateRowsAtIndexPaths:withRowAnimation: methods was wrong. I had been keeping track of where things should be added and removed in the table and adjusting the indexes as appropriate, but it turns out that's not necessary. Inside a table update block, one only needs to reference the index of a row from before the update, regardless of how many may be inserted or deleted to change its position. The update block, apparently, does all that bookkeeping for you. (Go to about the 8:45 mark in the video for the key example).
Thus, the delegate method that updates the table for the number of entries passed to it by the parser (currently 10-at-a-time) now explicitly tracks the positions of rows to be updated or deleted from before the update block, like so:
NSMutableDictionary *oldIndexFor = [NSMutableDictionary dictionaryWithCapacity:posts.count];
int i = 0;
for (PostModel *e in posts) {
[oldIndexFor setObject:[NSNumber numberWithInt:i++] forKey:e.ident];
}
NSMutableArray *insertPaths = [NSMutableArray array];
NSMutableArray *deletePaths = [NSMutableArray array];
NSMutableArray *reloadPaths = [NSMutableArray array];
BOOL modified = NO;
for (PostModel *entry in entries) {
NSNumber *num = [oldIndexFor objectForKey:entry.ident];
NSIndexPath *path = [NSIndexPath indexPathForRow:currentPostIndex inSection:0];
if (num == nil) {
modified = YES;
[insertPaths addObject:path];
[posts insertObject:entry atIndex:currentPostIndex];
} else {
// Find its current position in the array.
NSUInteger foundAt = [posts indexOfObject:entry];
if (foundAt == currentPostIndex) {
// Reload it if it has changed.
if (entry.savedState != PostModelSavedStateUnmodified) {
modified = YES;
[posts replaceObjectAtIndex:foundAt withObject:entry];
[reloadPaths addObject:[NSIndexPath indexPathForRow:num.intValue inSection:0]];
}
} else {
// Move it.
modified = YES;
[posts removeObjectAtIndex:foundAt];
[posts insertObject:entry atIndex:currentPostIndex];
[insertPaths addObject:path];
[deletePaths addObject:[NSIndexPath indexPathForRow:num.intValue inSection:0]];
}
}
currentPostIndex++;
}
if (modified) {
[tableView beginUpdates];
[tableView insertRowsAtIndexPaths:insertPaths withRowAnimation:UITableViewRowAnimationTop];
[tableView deleteRowsAtIndexPaths:deletePaths withRowAnimation:UITableViewRowAnimationBottom];
[tableView reloadRowsAtIndexPaths:reloadPaths withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
Comments welcome. It's entirely possible that there are more efficient ways to do this (the use of -[NSArray indexOfObject:] is particularly suspicious to me), and that I may have missed some other subtlety.
But even so, this is a huge improvement for my app. The UI now stays (mostly) responsive during a sync, the sync is fast, and the table update animation looks just about right.
Have you tried [tableView beginUpdates]; and [tableView endUpdate];?