I'm trying to move from one NSManagedContext to multiple via nested context.
I'm using those article to help me :
http://www.cocoanetics.com/2012/07/multi-context-coredata/
http://floriankugler.com/blog/2013/4/2/the-concurrent-core-data-stack
Actual System
[myHTTPClient getPath:path
parameters:#{access_token & stuff}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
for (NSDictionary *dictionary in responseObject)
{
// filling the dic in a NSManagedObject
}
[myMainContext save:&error];
}, failure:failureBlock];
Wanted System
[myHTTPClient getPath:path
parameters:#{access_token & stuff}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
__block NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
__block NSManagedObjectContext *writerObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] writerManagedObjectContext];
__block NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = managedObjectContext;
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
[temporaryContext performBlock:^{
for (NSDictionary *dictionary in responseObject)
{
// filling the dic in a NSManagedObject
}
[temporaryContext save:&error];
[managedObjectContext performBlock:^{
[managedObjectContext save:&error];
[writerObjectContext performBlock:^{
[writerObjectContext save:&error];
}];
}];
}];
}, failure:failureBlock];
Diagram
Problem
I had problems saving my data while using this method but I solved it (cf. https://stackoverflow.com/questions/18151827/coredata-writermanagedobjectcontext-freeze-when-save)
However, as I said in the previous question, it speed up 2 times on the simulator but it's like 5x slower on a device. I use iPod Touch 4th to test.
How is it possible and how can I improve that ?
I'm not against using this diagram :
But I want to find out HOW to integrate that in my code because I tried and it changed nothing.
Thank you.
Related
How I can delete an object which I had added before with this code. Its a favorites section, in the begin, I add a gray star which adds an object coming from a fetch. Then It turns yellow and the backwards method should be star yellow = deletes.
But I have no idea how to do this.
-(IBAction)inFavoris:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *favorisObj = [NSEntityDescription
insertNewObjectForEntityForName:#"Favoris"
inManagedObjectContext:context];
[favorisObj setValue:idTaxi forKey:#"idTaxi"];
[favorisObj setValue:nomTaxi forKey:#"nomTaxi"];
[favorisObj setValue:taxiCB forKey:#"cb"];
[favorisObj setValue:taxiAvion forKey:#"avion"];
[favorisObj setValue:taxiColis forKey:#"colis"];
[favorisObj setValue:taxiHandicape forKey:#"handicape"];
[favorisObj setValue:taxiHoraires forKey:#"horaire"];
[favorisObj setValue:lugagge forKey:#"lugagge"];
[favorisObj setValue:luxury forKey:#"luxury"];
[favorisObj setValue:languesParlees forKey:#"langues"];
[favorisObj setValue:taxiNote forKey:#"note"];
[favorisObj setValue:taxiPassengers forKey:#"passenger"];
[favorisObj setValue:taxiVote forKey:#"etoiles"];
[favorisObj setValue:taxiTel forKey:#"tel"];
[self.view addSubview:favorisB];
}
UPDATE
I made this method.. It gets the job done :)
-(IBAction)outFavoris:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSString *testEntityId = idTaxi;
NSManagedObjectContext *moc2 = [appDelegate managedObjectContext];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
fetch.entity = [NSEntityDescription entityForName:#"Favoris" inManagedObjectContext:moc2];
fetch.predicate = [NSPredicate predicateWithFormat:#"idTaxi == %#", testEntityId];
NSArray *array = [moc2 executeFetchRequest:fetch error:nil];
for (NSManagedObject *managedObject in array) {
[moc2 deleteObject:managedObject];
}
[self.view addSubview:favorisO];
}
Its quite simple :)
[context deleteObject:favorisObj];
And the bad object is all gone.
Update
You'd just reverse it with something like this if you need a button to delete the object.
-(IBAction)removeFavoris:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
[context deleteObject:favorisObj];
}
Don't forget to save the Context after you have deleted a NSManagedObject. So here is the general code;
NSManagedObjectContext * context = [self managedObjectContext];
[context deleteObject:objectToDelete];
NSError * error = nil;
if (![context save:&error])
{
NSLog(#"Error ! %#", error);
}
In your case it should have the snippet after the for loop.
for (NSManagedObject *managedObject in array) {
[moc2 deleteObject:managedObject];
}
NSError * error = nil;
if (![context save:&error])
{
NSLog(#"Error ! %#", error);
}
The app needs to retrieve data from core-data then compare one of the values with an IF STATEMENT
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *getDetails = [NSEntityDescription entityForName:#"Detail" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:getDetails];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(genID = %i)", genID];
[request setPredicate:pred];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"genID" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
for (NSManagedObject *oneObject in objects) {
NSString *type = [oneObject valueForKey:#"type"];
if (type == #"straight") {
NSLog(#"straight");
//straight logic removed
}else if (type == #"Left") {
NSLog(#"Left");
//Left logic removed
}else {
NSLog(#"Else");
//Else logic removed
}
}
The issue is that it never goes into "straight" or "left" always the ELSE. Stepping through the code I can see that values do match straight and left, even dumped them into a log file shows them fine. What is wrong?
You need to compare strings using isEqual: or isEqualToString: or compare:.
For instance:
NSString *type = [oneObject valueForKey:#"type"];
if ([type isEqualToString:#"straight"])
// etc.
Search StackOverflow for "NSString equal" or "NSString compare" if you need more explanation. You aren't anywhere near the first person to ask this question, and you definitely won't be the last.
I having trouble with my threads.
After i segue a couple of times between 2 screen when the thread is busy. The thread don't perform every line.., The breakpoint just disappear when it has to return to the main thread.
Can somebody please help me ?
I release the thread when the view is unload.
Thanks,
- (void)fetchFeedDataIntoDocument
{
NSString * labelString = [NSString stringWithFormat:#"Feed Fetcher %#", self.pageTitle];
const char *label = [labelString UTF8String];
self.fetchF = dispatch_queue_create(label, NULL);
dispatch_async(self.fetchF, ^{
NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:#"up"];
NSDictionary *lastfeed;
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate getManagedObjectContext];
if ([feeds count] > 0)
{
lastfeed = [feeds objectAtIndex:0];
[FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:#"id"] AndPulishDate:[lastfeed objectForKey:#"publish_up"]];
}
for (NSDictionary *feedInfo in feeds) {
[Feed FeedWithInfo:feedInfo InManageObject:context];
}
NSError *error = nil;
[context save:&error];
if (error){
NSLog(#"Error save : %#", error);}
dispatch_async(dispatch_get_main_queue(), ^{
[self setupFetchedResultsController];
[self.tableView reloadData];
[self downloadImagesForFeeds:feeds];
});
});
You are accessing the managedObjectContext from a different thread from where it was created. This is Core Data Threading Rule #1.
You are getting the MOC from the app delegate. If it's the normal Xcode-generated MOC, then it is created with thread-confinement concurrency. You can't even call performBlock with it. You can only access that MOC from the main thread. Period. Anything else is playing with fire, at best.
If you want to do all the work in a separate thread, you need a separate MOC as well. Like this (just typed - not compiled)...
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = appDelegate.managedObjectContext;
[moc performBlock:^{
// Go get your remote data and whatever you want to do
// Calling save on this MOC will push the data up into the "main" MOC
// (though it is now in the main MOC it has not been saved to the store).
[moc save:&error];
}];
Which would translate into something like this...
- (void)fetchFeedDataIntoDocument
{
NSString * labelString = [NSString stringWithFormat:#"Feed Fetcher %#", self.pageTitle];
const char *label = [labelString UTF8String];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate getManagedObjectContext];
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = mainContext;
[context performBlock:^{
NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:#"up"];
NSDictionary *lastfeed;
if ([feeds count] > 0)
{
lastfeed = [feeds objectAtIndex:0];
[FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:#"id"] AndPulishDate:[lastfeed objectForKey:#"publish_up"]];
}
for (NSDictionary *feedInfo in feeds) {
[Feed FeedWithInfo:feedInfo InManageObject:context];
}
NSError *error = nil;
[context save:&error];
if (error){
NSLog(#"Error save : %#", error);}
DO you really want to continue on error?
dispatch_async(dispatch_get_main_queue(), ^{
// Data has been pushed into main context from the background
// but it still needs to be saved to store...
// Do not forget to perform error handling...
NSError *error = nil;
[mainContext save:&error];
[self setupFetchedResultsController];
[self.tableView reloadData];
[self downloadImagesForFeeds:feeds];
});
});
EDIT
The code generated by Xcode for creating the MOC uses init, which uses NSConfinementConcurrencyType. You can replace it with MainConcurrency, without any problems, but get several benefits.
In your app delegate file, replace...
__managedObjectContext = [[NSManagedObjectContext alloc] init];
with this...
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
Now, your main MOC can be "parented" and you can also call performBlock on it as well.
what about doing this...
-(void)functionToCallFetch {
[self performSelectorInBackground:#selector(fetchFeedDataIntoDocument) withObject:nil];
}
- (void)fetchFeedDataIntoDocument
{
NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:#"up"];
NSDictionary *lastfeed;
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate getManagedObjectContext];
if ([feeds count] > 0)
{
lastfeed = [feeds objectAtIndex:0];
[FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:#"id"] AndPulishDate:[lastfeed objectForKey:#"publish_up"]];
}
for (NSDictionary *feedInfo in feeds) {
[Feed FeedWithInfo:feedInfo InManageObject:context];
}
NSError *error = nil;
[context save:&error];
if (error){
NSLog(#"Error save : %#", error);}
//dispatch_async(dispatch_get_main_queue(), ^{
// [self setupFetchedResultsController];
// [self.tableView reloadData];
// [self downloadImagesForFeeds:feeds];
//});
[self performSelectorOnMainThread:#selector(setupFetchedResultsController) withObject:nil waitUntilDone:NO];
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
[self performSelectorOnMainThread:#selector(downloadImagesForFeeds:) withObject:feeds waitUntilDone:NO];
}
Maybe that would work better?
Change following code..
dispatch_queue_t queue1 = dispatch_queue_create("com.MyApp.AppTask",NULL);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue1,
^{
dispatch_async(main,
^{
[self setupFetchedResultsController];
[self.tableView reloadData];
[self downloadImagesForFeeds:feeds];
});
});
Hope, this will help you
Did you try building a method like :
- (void)methodToBePerformedOnMainThread{
[self setupFetchedResultsController];
[self.tableView reloadData];
[self downloadImagesForFeeds:feeds];
}
and call it with
[self performSelectorOnMainThread:#selector(methodToBePerformedOnMainThread) withObject:nil waitUntilDone:NO];
at the end of fetchFeedDataIntoDocument
Edit :
Did you try to wrap it with NSOperationQueue in place of dispatch_queue ?
Like :
NSOperationQueue *operationQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(fetchFeedDataIntoDocument) object:nil];
if(operation != nil){
[operationQueue addOperation:operation];
I have an app which is a UITabBarController, I have defined two subviews
Both tabs have their Class attribute in the Identity Inspector set to UINavigationController.
Now i have managed to get this far with my coding after VERY LONG trials.
- (void)viewDidLoad {
[super viewDidLoad];
myAppDelegate *appDelegate = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = appDelegate.managedObjectContext;
{
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"User" inManagedObjectContext:self.managedObjectContext]];
NSArray *fetchedItems = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSEntityDescription *entityDesc =
[NSEntityDescription entityForName:#"User" inManagedObjectContext:self.managedObjectContext];
// replace the old data with new. this DOESNT WORK
if (fetchedItems.count > 0)
{
Usr *newUsr;
for (newUsr in fetchedItems)
{
if ([newUsr.name isEqualToString:#"Line One"])
{
newUsr.uName = #"Line One (new)";
}
}
}
//add a new default data. THIS ADDS DATA TO MY TABLEVIEW BUT IT DOESNT SAVE THEM TO THE SQLITE
User *addedDefaultdata = nil;
addedDefaultdata = [[User alloc] initWithEntity:entityDesc insertIntoManagedObjectContext:self.managedObjectContext];
addedDefaultdata.name = #"Added new 1";
[addedDefaultdata release];
}
NSError *error = nil;
if (![User.managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
and my appdelegate looks like this:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[application setStatusBarStyle:UIStatusBarStyleBlackOpaque];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
}
now I cannot quire the "User" at all! although i get no errors or warnings!
Any suggestions would be much appreciated!
Thanks
It seems that you may be asking how to update to CoreData?
If so, you need to use the save: method on NSManagedObjectContext, like this:
NSError *error;
[managedObjectContent save:&error];
if (error) {
...
}
I keep getting a -> Program received signal: “EXC_BAD_ACCESS”.
In the following code but I don't really understand why.
If I comment out the "executeFetchRequest" lines it goes away.
Shouldn't the [results release]; be all that's required?
Thanks in advance,
Matt
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// fetch the delegate
TestingAppDelegate *app = (TestingAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = [app managedObjectContext];
// construct the request
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Client" inManagedObjectContext:managedObjectContext]];
NSError *error;
NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];
[results release];
}
I believe results, the result of executeFetchRequest:error:, should already be autoreleased. Because you are explicitly calling [results release], you're over-releasing that object when the current autorelease pool is drained. Remove the [results release] line and see if that fixes it.