Why is this in the FetchedResultsController code that Apple gives us?
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
This is because they only need to set up the fetchedResultsController once.
Below that statement there is a load of set up to initialise fetchedResultsController.
So the first time you hit the method by calling fetchedResultsController the ivar is set up then any subsequent calls to fetchedResultsController simply return the already set up ivar.
Related
Just a little question...
When performing CoreData requests (in objective-C), do I have to make them on the context queue explicitly, or are they assumed to do that by themselves?
In other words can I just write:
NSArray *results = [context executeFetchRequest:request error:nil];
Or do I have to write (if I want to avoid issues):
__block NSArray *results = nil;
[context performBlockAndWait:^{
results = [context executeFetchRequest:request error:nil];
}];
Thanks!
You can never assume that Core Data will use the correct queue. You always need to tell it via one of the “perform” methods. The only exception is if you have a main-queue context and you know your code is running on the main queue. You can skip “perform” in that one and only case.
You mention using a convenience method to do a fetch, using “perform” inside the method. That’s fine, but don’t forget that you also need to use “perform” any time you use any of the fetched objects. For example, you can’t use property values from fetched objects unless you also use “perform” (again with the one exception I mentioned).
So just to be sure Tom:
All my CoreData objects (subclasses of NSManagedObject) have their CoreData properties as private (with a prefix "cd_"). I never expose them and use wrapper properties instead:
- (nullable NSString *) email {
return self.cd_email;
}
I've created a method on NSManagedObject:
- (id) safeValueForProperty: (SEL)property {
__block id value = nil;
[self.managedObjectContext performBlockAndWait:^{
value = [self valueForKey:NSStringFromSelector(property)];
}];
return value;
}
So now I can use (from any thread in theory):
- (nullable NSString *) email {
return [self safeValueForProperty:#selector(cd_email)];
}
(and of course the same for setters).
Am I doing right?
I have a model with this one to many relationShip:
Order -->> LineItem
I display LineItems in UITableViewCells:
I use UIPickerView for changing quantity of LineItems.
GOAL=> by changing picker value, subTotal be recalculated again.
the problem is here by updating lineItem, NSFetchedResultsController Delegate doesn't call (where I can reconfigure the cell again and display updated data). but when I update Order e.g set it as completed NSFetchedResultsController Delegate methods will be called.
why by updating lineItem doesn't affect delegates methods to be called?
I use magicalRecord and here is how I get NSFetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
else
{
_fetchedResultsController = [Order fetchAllSortedBy:#"orderDate" ascending:YES withPredicate:nil groupBy:nil delegate:self];
}
return _fetchedResultsController;
}
the way I setup table view:
ConfigureCellBlock configureCell = ^(OrderDetailsCell *cell, LineItem *lineItem)
{
[cell configureForLineItem:lineItem];
};
//set fetchedresults controller delegate
Order *order = [[self.fetchedResultsController fetchedObjects] lastObject];
NSArray *lineItems = [order.lineItems allObjects];
self.ordersDataSource = [[ArrayDataSource alloc] initWithItems:lineItems cellIdentifier:#"lineItemCell" configureCellBlock:configureCell];
self.tableView.dataSource = self.ordersDataSource;
configuring cell:
- (void)configureForLineItem:(LineItem *)lineItem
{
self.menuItemName.text = lineItem.menuItemName;
self.price.text = [lineItem.unitPrice stringValue];
self.quantity.text = [lineItem.quantity stringValue];
self.totalPrice.text = [lineItem.totalPrice stringValue];
self.pickerController.model = lineItem;
self.picker.delegate = self.pickerController;
self.picker.dataSource = self.pickerController;
[self.picker setSelectedNumber:lineItem.quantity];
}
does fetching obj1 then updating obj3 cause the NSFRC delegate methods to be called?
The FRC will only observe changes to the objects that it is directly interested in, not any of the objects that they are related to.
You should configure your own observation, either directly with KVO or to the context being saved, and use that to trigger a UI refresh.
I have an NSOperationQueue that has some NSBlockOperations added to it, among which are blockOperations A and B. The NSOperationQueue has a maxConcurrencyOperationCount of 1.
blockOperation B, is dependant on A being finished. In each of the blockOperations I am calling a method, which in turn calls another method, that initialises a new NSManagedObjectContext (with the persistentStoreCoordinator from a singleton), that I use to create and add objects to a Core Data database. The code invoked by the aforementioned second method call in A and B looks like this (it varies slightly for for each of them):
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
managedObjectContext.persistentStoreCoordinator = [[CoreDataController sharedCoreDataController] persistantStoreCoordinator];
for (NSDictionary *articleDictionary in array) {
if (![Article articleExistsWithIDInDictionary:articleDictionary inContext:managedObjectContext]) {
[Article articleFromDictionary:articleDictionary inContext:managedObjectContext];
}
}
[[CoreDataController sharedCoreDataController] saveContext:managedObjectContext];
// method ends.
The saveContext: code looks like this:
NSError *error = nil;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
Having spent a lot of time reading Apples Docs about Core Data Concurrency, NSOperation, etc., I'm still unsure if what I'm doing with NSManagedObjectContext is thread-safe, and generally considered to be OK? Some clarification and/or indication of that I should be doing differently would be much appreciated. If you need to see any more code, please ask.
Thanks in advance.
What you are doing is NOT thread safe.
If you decide to create a context per operation, you better use the confinement concurrency type:
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
this way you don't need to change anything in your current code.
if you want to use the context with NSPrivateQueueConcurrencyType you must access objects within that context by the performBlock: or performBlockAndWait::
[managedObjectContext performBlockAndWait:^{//wait is used as to not end the operation before this code is executed
for (NSDictionary *articleDictionary in array) {
if (![Article articleExistsWithIDInDictionary:articleDictionary
inContext:managedObjectContext])
{
[Article articleFromDictionary:articleDictionary
inContext:managedObjectContext];
}
}
}];
I would probably go with my first solution in your case.
all that said, you could simply use a "private queue" context as a serial queue (as long as you add block operation to it in the order you need them to execute).
A context performBlock: method will queue the block and execute it serially in regard to other blocks added that context for execution in the background:
//add this to your CoreDataController
context = [[CoreDataController sharedCoreDataController] serialExecutionBGContext];
[context performBlock:^{ //your block operation code1}];
[context performBlock:^{ //your block operation code2}];
this will perform code1 and code2 in the background serially.
In this manner, you save the overhead of allocating a new context, and might benefit from caching done by this context.
You might want to reset this context every now and then so it will not get bloated with fetched objects.
The concern with the context is that it be accessed only within a single thread. Setting the MaxConcurrencyOperationCount does not guarantee that. Another approach is to make the context a "thread" variable, storing a context in each thread dictionary where it is used.
Ex:
+ (NSManagedObjectContext*)managedObjectContext
{
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext *context = [threadDictionary valueForKey:#"QpyManagedObjectContext"];
if (context == nil) {
#autoreleasepool {
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[context setStalenessInterval: 10.0];
[context setMergePolicy:[[NSMergePolicy alloc]initWithMergeType:NSOverwriteMergePolicyType]];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[Qpyd managedObjectModel]];
[context setPersistentStoreCoordinator:coordinator];
NSString *STORE_TYPE = NSSQLiteStoreType;
NSString *path = [[NSProcessInfo processInfo] arguments][0];
path = [path stringByDeletingPathExtension];
NSURL *url = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:#"sqlite"]];
NSError *error;
NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE configuration:nil URL:url options:nil error:&error];
if (newStore == nil) {
NSLog(#"Store Configuration Failure %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
}
[threadDictionary setObject:context forKey:#"QpyManagedObjectContext"];
}
}
return context;
}
Generally speaking... in an NSManagedObject class, inside one of the setters for a given float dynamic property, is it OK to use the dot-syntax getters for other float dynamic properties of the same NSManagedObject within that setter? Or do I need to use KVC-compliant CoreData accessors any time I access a value, even if it's from a different method than the actual getter for the value being accessed? I would assume that calling the dot-syntax in this way would cause my other custom accessor to fire, which is OK with me, since inside that there are the proper KVC primitive accessors. But I've seemed to run into weird issues where the dot-syntax either simply fails, or seems to have unpredictable results, and I'm not sure if it's because of the fact I'm using the dot-syntax in an unsafe way, or if there's some other bug I haven't figured out yet.
Here's a code sample of something I'm talking about:
- (void)illTakeYouToTheWoodshed {
float h = self.SSx.floatValue/self.yourMomsCurrentWeightInTons.floatValue;
[self willChangeValueForKey:#"SSy"];
[self setPrimitiveValue:#(h) forKey:#"SSy"];
[self didChangeValueForKey:#"SSy"];
[self diagonal]; //makes sure nd gets set
}
- (void)setSSx:(NSNumber *)value{
[self willChangeValueForKey:#"SSx"];
[self setPrimitiveValue:value forKey:#"SSx"];
[self didChangeValueForKey:#"SSx"];
if(self.WorH==syanara || self.WorH == dude_wtf) {
if(self.SSy.floatValue != 0.0) {
[self doThatFunkyDance];
[self diagonal];
} else if (self.youBetcha.floatValue != 0.0) {
[self whatTheFrakDoesThisEvenDo];
}
} else if (self.WorH==fooBarTastic) {
if(self.yourMomsCurrentWeightInTons.floatValue != 0.0) {
[self illTakeYouToTheWoodshed];
}
} else {
NSLog(#"Escaped with salad not having been tossed.");
}
}
The Title is the whole question. If the _asych block of code produces meaningful work it will in some cases have produced information which the main thread would now like to use.
In this bare example, how would you get the data value, the string data, contained in myData out of the block for the main thread to work with:
dispatch_queue_t myQueue = dispatch_queue_create("com.mycompany.myqueue", 0);
dispatch_async(myQueue, ^{
NSString *myData = [self getSavedData];
});
dispatch_async(myQueue, ^{ dispatch_release(myQueue); });
Please extend the code help to show me, in simple usage, where and how this NSLog, or its correct equivalent, would be placed in the main thread of the program relative to the GCD block:
NSLog(#"%#", myData);
You can nest blocks, yet have each run in different threads.
dispatch_queue_t myQueue = dispatch_queue_create("someid", 0);
dispatch_async(myQueue, ^{
NSString *myData = [self getSavedData];
dispatch_async(dispatch_get_main_queue(), ^{
self.someLabel.text = myData;
});
});
dispatch_async(myQueue, ^{ dispatch_release(myQueue); });
If your code is long, it's unwieldy to have in nested blocks. So simply call a method inside dispatch_async like [self processData:myData].