merge ManagedobjectContext in multi threads - iphone

My app have main thread which run every ten second and read data from core data and second thread to fetch data from server and load into coredata. after reading some example and also apple document I am still not sure the way i merge is correct:
1. i create new managedObjectContext for each save in database and set it to (single PersistentStore in appp delegate) .
2. have this code when i initate the class for loading data in user view:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:appDelegate.managedObjectContext];
-(void)mergeChanges:(NSNotification *)notification
{
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
3- read data from coredata from main context
I am wondering if this is correct.
thanks

Related

Reload UITableView from another tab

I am having trouble trying to reload UITableView cell data which are being loaded from an XML source.
Here is the scenario. App contains tabs, in one of them there is a tableview which gets it's data from an XML file and works just ok, but the thing is when I want to change the feed category and change the XML from another tab I can refresh the current tableview.
For switching between tabs I use
self.tabBarController.selectedIndex = 1;
and pass the category feed to the other tab which I want to load
xmlParser = [[XMLParser alloc] loadXMLByURL:categories];
and it still loads the same old feed, not the new one which has been passed. I checked with NSLog and the feed value passes properly but it just wont load after switching.
I also tried to [self.tableView reloadData]; from both current tab and the category tab and it didn't work either.
You can use NSNotifications to send a notification from your other tab and have a oberver in your tableview that responds to that notification.
Example
(Tab calling the reload of the tableview) put this code whenever you want to reload the data, so when a button is pushed or a download is finished etc.
NSNotification * notification = [NSNotification notificationWithName:#"updateTable" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:notification];
In the UITableViewController / the class with the UITableView, do the following.
in your viewDidLoad add:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateTableView) name:#"backtolist" object:nil];
Then add the function updateTableView
- (void) updateTableView: (NSNotification*) note
{
//Do whatever needs to be done to load new data before reloading the tableview
[_tableView reloadData];
}
Hope this helps
Ophychius was correct in his suggestion to use Notifications. I'm assuming you have all of the data sources for your table view updating when the XML is finished loading. This also assumes you're using dynamic cells. In the class that loads the XML, post a Notification when the new XML is finished loading.
[[NSNotificationCenter defaultCenter] postNotificationName:#"XMLLoaded" object:nil];
In the table view class, register as an observer for the Notification you posted from the XML class.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadTable:) name:#"XMLLoaded" object:nil];
As you can see, this calls a selector when this notification is received. Either call your method where you build the table, or create another simple method to call reloadData from.
-(void)reloadTable:(NSNotification *)notif
{
NSLog(#"In ReloadTable method. Recieved notification: %#", notif);
[self.tableView reloadData];
}
Finally (as Leonardo pointed out below), in your viewDidUnload (or dealloc for ios6) method, remove the class as an observer of that notification.
- (void)viewDidUnload
{
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
I am just guessing, without having seen the rest of the code.
I suppose your table view has an NSArray datasource, did you make sure that your array datasource is updated too ? Does your xml parser, or controller, transfer those data to the NSArray ?
Because if you call reloadData it is just going to refetch the same array. And if it is not updated, you would get old data.

how to call function from another class and reload that view from the current class?

I am working on an application which uses bump technology.I do have four tab in which one is a table view .I wrote this bump API in app delegate class so that when the application is open it should be able to transfer the data.Transfer function is working properly.But the problem is that I am inserting the data into sq-lite and the data from sqlite is displayed in one of the tab bar item view.So when the user selects this tab bar item and receives the data i would like to insert and also reload the view with the new changes.As told before insertion i working.But the problem is reloading the view.Can any one help me with this problem?
You can perform insertion in background using NSOperation and post notification whenever you insert/edit a record. Add listener to the View controller where you are displaying data.
So whenever the controller receive the notification, it will call the method to reload data from database.
#implementation MyClass
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (id) init
{
self = [super init];
if (!self) return nil;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadData:) name:#"COREDATA_OBJECT_EDITED" object:nil];
return self;
}
- (void) reloadData:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"COREDATA_OBJECT_EDITED"])
{
//load records from database and reload tableview
}
}
#end
//Method where you are saving data objects in some other class
- (void) saveDataObject
{
//Save Data object, if saved successfully then post notification to listener to reload the data
// All instances of MyClass will be notified
[[NSNotificationCenter defaultCenter] postNotificationName:#"COREDATA_OBJECT_EDITED" object:self];
}

Table view freezes when sql queries are fired in thread

I have attached a new thread and in that thread I'm firing 5 SQLite queries.
Problem is that until execution of all my queries is finished, I'm not able to scroll the table view. It freezes for some seconds.
-(void)viewDidAppear:(BOOL)animated
{
[NSThread detachNewThreadSelector:#selector(GetBackEndData)
toTarget:appDelegate withObject:nil];
}
// this is in appDelegate
-(void)GetBackEndData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if([dicBarnearMe_Detail count]==0)
{
// this are sql queries method.
[appDelegate SelectBeersonbottle_Count];
[appDelegate SelectBeersonTap_Count];
[appDelegate SelectGrowler_Count];
[appDelegate SelectHappyHours_Count];
[appDelegate SelectEvents_Count];
// After completing this process I'm post notification
// for reloading table in other controller.
[[NSNotificationCenter defaultCenter] postNotificationName:#"reload"
object:nil userInfo:nil];
}
[pool release];
}
You are making a new thread in viewDidAppear and executing the GetBackEndData selector in separate thread.
[[NSNotificationCenter defaultCenter] postNotificationName:#"reload"
object:nil userInfo:nil];
you making NSNotificationCenter in GetBackEndData and reloading some data that means you have to wait until your thread complete the execution and blocking UI thread.
making a thread in viewDidAppear is not a right approach you can use a dispatchQue in some other function or alternative option is wait until your thread execution get completed and show activity Indicator.

Passing Objects Between Classes

In my AppDelegate, I download some data from the internet and store it into an array. I want one of my ViewControllers to access that array. How would I go about in doing so? Is this a good situation to implement a delegate or a protocol? If so, can someone recommend a good tutorial for that?
Thank you
EDIT:
Please note that the data refreshes upon each launch so there is no need for Core Data or plists. Furthermore, the data are custom objects which I created so they can't be stored in a plist for example.
You have 2 options:
Implement a delegate protocol
Use NSNotifications
The advantages/disadvantages of each is set out well in this question and answer:
Delegates v Notifications
As notifications are easier to implement and may well be sufficient for your needs, you can implement it with the following steps:
In the class where you download the data:
When the data has been downloaded and the array populated, include the following lines:
NSDictionary *dict = [NSDictionary dictionaryWithObject:array forKey:#"Data"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataDownloaded" object:self userInfo:dict];
In the class where you want to receive the data:
2.1 Add the following line to your viewDidLoad method:
`[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataDownloaded:) name:#"DataDownloaded" object:nil];
2.2 Create the dataDownloaded selector:
(void)dataDownloaded:(NSNotification *)note {
NSDictionary *dict = note.userInfo;
NSArray *dataArray = [note.userInfo objectForKey:#"DataDownloaded"];
2.3 Add the following line to dealloc and viewDidUnload:
[[[NSNotificationCenter defaultCenter] removeObserver:self];
You can store data in plist file and use it in all view controllers. This way, you need not worry about the size of data (as you will load it on demand and free it immediately).
if you want to store your array in delegate then in any view you have to create reference of your delegate and you can access your array in that view like :
in your other view you have to write : in .h file
#import "YourDelegateFile.h" and declare a varialble
YourDelegateFile *appDelegate ;
in your .m file :
- (void) viewDidLoad
{
appDelegate = (YourDelegateFile *)[UIApplication sharedApplication] delegate];
NSArray *aArray = [appDelegate yourArrayName]; //yourArrayName is an array that you have declare in delegate
}
hope it helps you.
you just need to access the data stored within the appdelegate. I dont think this is the best solution to your problem but in order to do things the way you want.
so declare you property in your Appdelegate .h file
NSMutableArray* myArray_;
then add a property to the same file
#property (nonatomic, retain) NSMutableArray* myArray;
in the .m file
make sure you synthesize your property
#synthesize myArray = myArray_;
somewhere in your appdelegate .m file you will set the value
then, elsewhere in your code you can access the property in the appdelegate like so
MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate
NSMutableArray* localArray = appDelegate.myArray;
Note, for good encapsulation you should really use an NSArray but i used mutable to keep the code short.
Also, using the appdelegate as a global store for program data is not a good idea, it breaks a lot of rules you shouldnt break, single responsibility principle being a good one to start with. You should ideally be storing application data in a dedicated class, perhaps a singleton or for much better testability a single instance class served by a factory class. This way you data is accessible from a known well defined entity, it is testable and it honours good design principles
You can send notification if app delegate got new data and all interested controllers will know that they need to update views. For this you can use NSNotificationCenter. For example
- (void)newDataLoaded {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:arrayOfData forKey:#"data"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"data updated notification name" object:nil userInfo:userInfo];
}
If some controller interested in data updates it should subscribe for this notification as soon as possible:
- (void)viewDidLoad {
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataUpdatedNotificationHandler:) name:#"data updated notification name" object:nil];
...
}
Do not forget to unsubscribe from notifications if you don't need it. For this use [[NSNotificationCenter defautCenter] removeObserver:self] in viewDidUnload and dealloc methods.
- (void)dataUpdatedNotificationHandler:(NSNotification*)notification {
NSArray *data = [[notification userInfo] objectForKey:#"data"];
// update your view here
}

Generic approach to NSManagedObjectContext in multi-threaded application

I've read a number of posts here about NSManagedObjectContext and multi-threaded applications. I've also gone over the CoreDataBooks example to understand how separate threads require their own NSManagedObjectContext, and how a save operation gets merged with the main NSManagedObjectContext. I found the example to be good, but also too application specific. I'm trying to generalize this, and wonder if my approach is sound.
My approach is to have a generic function for fetching the NSManagedObjectContext for the current thread. The function returns the NSManagedObjectContext for the main thread, but will create a new one (or fetch it from a cache) if called from within a different thread. That goes as follows:
+(NSManagedObjectContext *)managedObjectContext {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
return moc;
}
// a key to cache the context for the given thread
NSString *threadKey = [NSString stringWithFormat:#"%p", thread];
// delegate.managedObjectContexts is a mutable dictionary in the app delegate
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
if ( [managedObjectContexts objectForKey:threadKey] == nil ) {
// create a context for this thread
NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] autorelease];
[threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
// cache the context for this thread
[managedObjectContexts setObject:threadContext forKey:threadKey];
}
return [managedObjectContexts objectForKey:threadKey];
}
Save operations are simple if called from the main thread. Save operations called from other threads require merging within the main thread. For that I have a generic commit function:
+(void)commit {
// get the moc for this thread
NSManagedObjectContext *moc = [self managedObjectContext];
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread] == NO) {
// only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
NSError *error;
if (![moc save:&error]) {
// fail
}
if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}
In the contextDidSave: function we perform the merge, if called by the notification in commit.
+(void)contextDidSave:(NSNotification*)saveNotification {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
[moc performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:YES];
}
Finally, we clean-up the cache of NSManagedObjectContext with this:
+(void)initialize {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(threadExit)
name:NSThreadWillExitNotification
object:nil];
}
+(void)threadExit {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *threadKey = [NSString stringWithFormat:#"%p", [NSThread currentThread]];
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
[managedObjectContexts removeObjectForKey:threadKey];
}
This compiles and seems to work, but I know threading problems can be tricky due to race conditions. Does anybody see a problem with this approach?
Also, I'm using this from within the context of an asynchronous request (using ASIHTTPRequest), which fetches some data from a server and updates and inserts the store on the iPhone. It seems NSThreadWillExitNotification doesn't fire after the request completes, and the same thread is then used for subsequent requests. This means the same NSManagedObjectContext is used for separate requests on the same thread. Is this a problem?
A year after posting this question I finally built a framework to generalize and simplify my working with Core Data. It goes beyond the original question, and adds a number of features to make Core Data interactions much easier. Details here: https://github.com/chriscdn/RHManagedObject
I found a solution after finally understanding the problem better. My solution doesn't directly address the question above, but does address the problem of why I had to deal with threads in the first place.
My application uses the ASIHTTPRequest library for asynchronous requests. I fetch some data from the server, and use the delegate requestFinished function to add/modify/delete my core-data objects. The requestFinished function was running in a different thread, and I assumed this was a natural side-effect of asynchronous requests.
After digging deeper I found that ASIHTTPRequest deliberately runs the request in a separate thread, but can be overridden in my subclass of ASIHTTPRequest:
+(NSThread *)threadForRequest:(ASIHTTPRequest *)request {
return [NSThread mainThread];
}
This small change puts requestFinished in the main thread, which has eliminated my need to care about threads in my application.