Relationships are (null) when -awakeFromInsert message received by new object in NSOrderedSet - core-data

The following -awakeFromInsert implementation sets properties of new objects instantiated by array controllers in a UI when the user presses an add button:
- (void)awakeFromInsert
{
[super awakeFromInsert];
NSLog(#"Adding perceptron %ld to layer %ld", self.indexInLayer, self.layer.indexInNetwork);
NSLog(#"New perceptron added to layer %#", self.layer );
// more code here to do the configuration
}
The problem is that the self.parent relationship of the new object is not set when -awakeFromInsert is called (it is nil) so I can't use it to access the parent object (for example how many childs there are or what index the new object is).
Output of the above code:
2012-12-17 21:36:39.309 MLPManager[98112:403] Adding perceptron 0 to layer 0
2012-12-17 21:36:39.309 MLPManager[98112:403] New perceptron added to layer (null)
I'm pretty sure that the new objects are being connected up correctly because the indexInLayer method works perfectly when the UITableView calls it to add indexes of the objects to the view:
- (NSUInteger)indexInLayer
{
NSUInteger index = [self.layer.perceptrons indexOfObject:self];
//NSLog(#"indexInLayer returning %ld", index );
return index;
}
My data model has three entities: Network, Layer, Perceptron arranged as ordered sets and connected to the next by to-many relationships (i.e. Parent - Child - Grandchild). My UI has three array controllers and three UITableViews. I've set it up so that the Child array controller only contains the children of the selected Parent, and the Grandchild array controller only contains the grandchildren of the selected Child. When I add childs or grandchilds to these arraycontrollers they are automatically set as children of the currently selected parent. That all works fine.
At what point is self.layer set by the UI? Can someone confirm that this is occurring after -awakeFromInsert? And if so, how am I supposed to configure a new object if I can't do it from within -awakeFromInsert? I note that the Apple documentation for -awakeFromInsert says it is "invoked automatically by the Core Data framework when the receiver is first inserted into a managed object context."
The reason I need information on the layer object and other parts of the data structure is that I need to automatically instantiate various other objects (weights which are children of perceptrons) at the same time as the new perceptron object. Should I be using -awakeFromInsert for these kind of tasks?

First, there appears to be coupling in your code between model (NSManagedObject) and view (UITableView) objects. That is not recommended according to the model-view-controller design pattern.
layer and indexInLayer are not standard attributes of NSManagedObject so I assume these are attributes in your entity. As an alternative to doing the setup in awakeFromInsert, I wonder if you can instead implement your own setter methods for your attributes so that you can do the necessary work at the time the required data is available.
If you choose to implement your own setter methods, you need to follow Apple's guidance in the Managed Object Accessor Methods documentation, specifically:
You must ensure that you invoke the relevant access and change
notification methods (willAccessValueForKey:, didAccessValueForKey:,
willChangeValueForKey:, didChangeValueForKey:,
willChangeValueForKey:withSetMutation:usingObjects:, and
didChangeValueForKey:withSetMutation:usingObjects:).
I do not know why layer and indexInLayer are not usable in your awakeFromInsert method; it's possible you need to solve that problem instead.

Related

How to bind non-UI entity with UI entity in Bevy

Description
I'm trying to implement trigger logic when the player faced the trigger I should remove the UI element from the screen.
Spawning the trigger point
/// Create a trigger point and when the user faced with
/// it I'll mark the tutorial as `in-progress` and
/// remove it when the collision between tutorial
/// and player is stopped
commands
.insert(Sensor(true))
.insert(Collider::cuboid(8.0, 8.0))
.insert(ActiveEvents::COLLISION_EVENTS)
.insert_bundle(SpriteBundle {
sprite: Sprite {
color: Color::rgb(0.1, 0.1, 0.1),
custom_size: Some(Vec2::new(16.0, 16.0)),
..Default::default()
},
transform: *tutorial_transform,
..Default::default()
})
// Tutorial is a component which I'll filter as `tutorial_entity`
.insert(Tutorial);
Create a UI
commands
.spawn_bundle(NodeBundle {
///
})
/// Trying to bind UI element with `Tutorial` entity
/// to remove it from the screen when the user faced with collider
.insert(Parent(tutorial_entity))
When the user faced collision
// I want to despawn all children UI elements that are linked with this non-UI element
commands.entity(tutorial_entity).despawn_recursive()
Error
I've got an error and no UI on the screen at all
Styled child in a non-UI entity hierarchy. You are using an entity with UI components as a child of an entity without UI components, results may be unexpected
Question
Do you know how to link a non-UI element with a UI element to remove the non-UI element and remove all linked UI elements with it?
I don't know if its still relevant but you could always just create a separate UI entity and add your own reference component.
I guess the for structurings sake i would create ( for example ) a UILinkComponent(pub Entity) and attach it to the world entity.
Pretty sure you shouldn't use Parent for that kind of relationship. The hierarchy is typically used for spatial relationships.
You should instead create a new component that stores the other Entity. You can still destroy it in the same way, but this way it isn't part of the hierarchy so the other UI elements don't get confused.

Clean way to get mousePressed event notified to QGraphicsView

I inherited from QGraphicsItemGroup and made a class that keeps a pointer to its contained items so that I can later refer to them and change properties. It has an ellipse item and a line item and I want only the ellipse to be clickable. I need that press event of the ellipse to propagate to the QGraphicsView so that I can send a signal to some surrounding widgets.
So far I tried inheriting also from QGraphicsObject to have signals available but got stuck with ambigous base error when trying to use scene->addItem. I tried casting to QGraphicsItemGroup but I still get the error. I also tried inheriting from QObject with no success.
I'm new to QGraphics and I know the QGraphics framework has a lot of tools for user interaction and even interaction between GraphicsItems but this is really kicking my butt.
What would be the proper way to get this behavior?
Create a separate "emitter" class
To allow your subclass of QGraphicsItemGroup to emit signals, you can create a separate "emitter" class that inherits from QObject. Then, you can add an instance of this emitter class within your subclass of QGraphicsItemGroup. The emitter object can then emit signals for your subclass as needed.
QGraphicsItemGroup is treated as a single item
Unfortunately, an instance of QGraphicsItemGroup is treated as a single item, so each mousePressEvent will belong to the entire group rather than one of the members of that group (i.e., the ellipse item or the line item). If you want the mousePressEvent to behave differently depending on which item is clicked, they will need to be separate items, or you could try using line->setParentItem(ellipse) to link up the 2 items without using QGraphicsItemGroup.

Disable Lazy-Load Entity Framework Navigation Properties

Question
Is there any way to make it so that an entity returned from a DbContext query returns null (or some other specific value) when you try to access a navigation property that you did not specificly .Include()? For example:
var parents = dbContext.People.Where(p => p.Children.Any()).Include("Children").ToList();
//Assert all parents have children...
Assert.IsTrue(parents[0].Children.Any());
And...
var parents = dbContext.People.Where(p => p.Children.Any()).ToList();
//Assert all children collections are null... NOT LAZY LOADED
Assert.IsTrue(parents[0].Children == null);
To be clear, I do not want the property to be Eager-Loaded. I don't want it to be loaded at all. I've tried Detaching the entity from the context, but this doesn't help.
Background
The reason I am trying to do this is because i need to access the entity object on a diffrent thread than the one the DbContext was created on. Because of this, I do NOT want the navigation property to be set to some defered excution linq statement. The problem is that since I cannot access DbContext to check if a navigation property is loaded or not (due to it not being thread safe) I have no way of knowing if I need to create a new DbContext on the current thread to retrieve the missing data. This is the same problem that I was trying to solve with How to tell if a Navigation Property is loaded without DbContext
Update
Setting the DbContext's Configuration.LazyLoadingEnabled property to false prevents the linq from getting auto-wired up, but for navigation properties that are collections this results in a empty collection rather than null.
To solve the collection based problem, I Modified my T4 template to generate an empty default constructor rather than one that set each ICollection equal to an empty HashSet.
You can enable / disable lazy-loading by setting the Configuration.LazyLoadingEnabled property of your DbContext
context.Configuration.LazyLoadingEnabled = false;
var parents = dbContext.People.Where(p => p.Children.Any()).ToList();
context.Configuration.LazyLoadingEnabled = true;

ViewModel for nested control not refreshing in Catel MVVM for WPF

I have a nested control (NC1) which contains 3 instances of NC2. When the containing view is first opened, NC1 will be null and so the three instances of NC2 will be null. This appears to work correctly based on debugging through my code and the framework.
When a selection is made in the containing view NC1 is properly set (and the ViewModel is (re)created) and values for its properties (exposed through Fody.Expose) appear in the view. However, none of the ViewModels for NC2 are (re)created and they do not reflect the values provided by their respective models.
I am not sure exactly what information to provide without uploading a lot of content, so I will take a stab.
In the NC1 view, I have the following
<localViews:NC2 DataContext="NC2Entry1"/>
<localViews:NC2 DataContext="NC2Entry2"/>
<localViews:NC2 DataContext="NC2Entry3"/>
NC2EntryX are properties on the NC1 ViewModel that return a specific instance of an NC2 model from a list. The NC2EntryX properties is NOT registered with RegisterProperty.
As a note, I have discovered that I must have an empty parameter constructor for the NC1 and NC2 view models. If I do not, then I receive a MissingMethod exception when the view model is being created when the TypeFactory attempts to create the ViewModel with the Activator instead of using the injection path. The injection path is not used because the call at line 591 of TypeFactory returns false because the NC2 model passed is 'not registered'. (Not sure if it should be or how to make it so.)
I am using Catel version 3.9.0
The NC2EntryX property changes must be reflected to the view somehow. You can do this by making it Catel properties, or by calling RaisePropertyChanged("NC2EntryX") yourself when setting the property value. Only in that case the view will be updated (this is just standard WPF / MVVM behavior).
About the missing method exception: it is a first chance exception where the TypeFactory tries to fall back to Activator.CreateInstance when it fails to create the type with dependency injection. No need to worry about this. If you don't want view models to be alive without a model, don't create an empty constructor.

How can I suspend the working of an NSFetchedResultsController?

I have a UITableViewController fed by an NSFetchedResultsController. From it, the user can call up a modal ViewController in which he or she can enter new data. As this begins, I create a temporary object as follows:
newPtr = [[Entry alloc] initWithEntity:[NSEntityDescription
entityForName:#"Entry" inManagedObjectContext:self.nmocontext]
insertIntoManagedObjectContext:self.nmocontext];
As the user makes choices, attributes of this 'provisional' object, newPtr, are set.
The problem is that the base UITableViewController remains active while the modal ViewController is visible. It seems to be freaking out (causing crashes) in some cases when it realizes a mandatory attribute of newPtr has not been set yet.
What can I do to stop the NSFetchedResultsController from looking at my managed object context until the modal ViewController is dismissed?
Core Data supports "nested" managed object contexts which allow for a flexible architecture that make it easy to support independent, cancellable, change sets. With a child context, you can allow the user to make a set of changes to managed objects that can then either be committed wholesale to the parent (and ultimately saved to the store) as a single transaction, or discarded. If all parts of the application simply retrieve the same context from, say, an application delegate, it makes this behavior difficult or impossible to support.
I haven't tested this myself but a possible approach would be to implement viewWillAppear and viewWillDisappear, and set the fetchedResultsController delegate to self on will appear and nil on will disappear.
OR
You could create an NSObject that mirrors the attributes of your NSManagedObject in your editing window. Once the user has finished editing the attributes (and you have run the appropriate validation rules) you can pass them back to your NSManagedObject instance and let the fetchedResultsController do its job.

Resources