observeValueForKeyPath: called for every property, not just the observed property - key-value-observing

I have an NSArrayController (itemsController) which hold an array of MyObject. This controller is tied to an NSTableView which has several columns, all bound to different properties.
I want to observe one of these properties elsewhere in the app.
[[self itemsController] addObserver:self forKeyPath:#"selectedObjects.someProperty" options:NSKeyValueObservingOptionNew context:nil];
In the callback:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
I am getting called whenever any property in the selection changes, but I only want to be called when someProperty in a selected object changes.
How can I prevent all these other calls when nothing has really changed?
Observing #"selection.someProperty" has the same results.

You also get notified when selectedObjects or selection changes. If you want to get notified when someProperty changes then you have to observe someProperty of the selected objects and remove and add observers when the selection changes.
The superclasses of your observing class could also be observing. Use the context parameter to recognize your notifications. See Best practices for context parameter in addObserver (KVO)

Related

UITableView not loading data the second time

I have a UITableView and I call reloadData in the viewWillAppear method
- (void)viewWillAppear:(BOOL)animated {
[tableView reloadData];
}
However, the second time my view appears, neither numberOfRowsInSelection nor cellForRowAtIndexPath are called
What could I be doing wrong?
I am seeing the same problem and yes, using a breakpoint I have already confirmed that viewWillAppear is called and reloadData executed - but cellForRowAtIndexPath is never called and the table contains stale data.
New information: I tried creating a new class/nib, this time using a UITableViewController instead of a UIViewController and implementing the delegate & datasource interfaces manually. This works as expected - cellForRowAtIndexPath is called and the table is updated. There must be something else UITableViewController does for me that I need to implement in my version, but I don't know what it is....
Solved! My issue, anyhow. I had wired up the datasource and controller connections, but hadn't connected the tableView outlet in my class to the table in the UI builder. smacks forehead. Mine works now, hope this helps!

Getting UITabBarController to work with Core Data

I've been reading this thread on Stackoverflow and have been trying to replicate the solution with no success in my own project.
My project has 4 tabs. In my app delegate I do this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
Page1 *page1 = (Page1 *)[navController topViewController];
Page2 *page2 = (Page2 *)[navController topViewController];
Page3 *page3 = (Page3 *)[navController topViewController];
Page4 *page4 = (Page4 *)[navController topViewController];
page1.managedObjectContext = self.managedObjectContext;
page2.managedObjectContext = self.managedObjectContext;
page3.managedObjectContext = self.managedObjectContext;
page4.managedObjectContext = self.managedObjectContext;
[self.window makeKeyAndVisible];
return YES;
}
In the originating thread it says I need to create a IBOutlet to each navController for each tab I want to use Core data on.
Whilst you can assign multiple delegates for the UINavigationController the same is not true for the outlets, you can only ever supply ONE outlet for the navController.
I can get Page1 to work, but the other pages simply crash; because of the lack of an IBOutlet.
Do I really need X IBOutlets for Y Tabs or can I do it another way?
Another issue is that the originating thread the accepted answer is:
Ideally you want to pass either the
NSManagedObjectContext,
NSFetchedResultsController or the
relevant NSManagedObject "down" into
the UIViewController.
But there is no code or example of how to do this.
Ideally, I do not want to use a singelton or use the app delegate all over the place.
Any confirmation and clarification would be great.
Thanks.
Your immediate problem has nothing to do with Core Data. You are assigning the same navigation controller to each tab when you need a separate navigation controller for each tab otherwise the navigation controller's hierarchy of views will get scrambled every time you change tabs.
The pattern recommended in the question you linked to is called "dependency injection" and it is the one that Apple recommends in most cases. However, in the case of tabbars or any other complex view/view-controller hierarchy, dependency injection can get to complicated. It's a particular issue with tabbars because you don't usually load all tab view/view-controllers when the app starts but wait until each tab is selected before loading its elements.
Instead, you can use an alternative pattern that exploits the UIApplication objects singleton status. Since there is only one application object, there is only one application delegate object. That means that anywhere in the app you can make a call like this:
(MyApplicationDelegate *) appDelegate=(MyApplicationDelegate *)[[UIApplication sharedApplication] delegate];
... and always get the same application object. Then, if you have the managed object context defined as a property of the app delegate you can get the context just by:
theManagedObjectContext=appDelegate.managedObjectContext
Add these two lines to every view controller and you can always be sure of getting the app delegate's managed object context.

MonoTouch/MT Garbage Collector: how to correctly release my views/class members (simple example)?

Have a look at the sample below which is not completely implemented but demonstrates what I'm talking about:
public class MyClass : UIView
{
private UIView SubView;
private UILabel ALabelInTheSubView;
public override ViewDidLoad()
{
}
// Imagine our view has a button. When clicked, a new sub view is added.
public void HandleButtonAddView()
{
// Assign a new view to the SubView member and add a label.
this.SubView = new UIView();
this.ALabelInTheSubView = new UILabel("A label");
this.SubView.AddSubView(this.ALabelInTheSubView);
// Add the SubView to myself.
this.AddSubView( this.SubView );
}
// Imagine our view has another button. When clicked, the sub view is removed.
public void HandleButtonRemoveView()
{
this.SubView.RemoveFromSuperView();
}
}
If I click the button to add the subview, the member variables SubView and ALabelInTheSubView are assigned new instances of UIView and UILabel.
If I then click the button to remove the subview, SubView gets removed from super view.
After this.SubView.RemoveFromSuperView() the members SubView and ALabelInTheSubView still have references to the view or label, hence no memory will be released yet. Correct so far?
If I now click the button to add the sub view again, the members will be overwritten with NEW instances of UIView and UILabel.
QUESTION: Does the GC now know that it can now safely dispose the previously assigned UIView and UILabel? I mean, all references should be gone. Or do I have to call this.SubView.Dispose() and this.ALabelInTheSubView.Dispose() after removing from superview? And is disposing the label necessary at all, since it is a child node of the UIView, which just got removed and disposed (which would mean I always have to dispose from bottom to top)?
ADD. SIDE-QUESTION:
If I call Dispose() on an object this IS still referenced - is that a a problem?
The answer is yes, the garbage collector will now consider the two previous instances of the objects to not be reachable from anywhere, and will be collected the next time the GC runs.
You do not need to call Dispose() manually. The only reason to call Dispose() manually is in cases where you know that the object you are pointing to is some large object and you want to make sure that the memory is released immediately, and not wait for the GC to kick-in at some point in the future.
What happens when you call Dispose () on the object, is that we internally release the reference to the Objective-C object and we set the Handle property on the object to null. This means that if you try to call another method on a disposed object, you will get a nice exception.
I would just reuse your SubView instead of making a new one each time. So create it in ViewDidLoad, store it in a member variable, and reuse it.
I don't think the extra memory will be a problem, and you won't have to worry about the GC at all.

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.

tableview methods are been called only Once in Objective C on iphones?

i have tableview.In method 'viewWillAppear',it calls a function and function data is stored in an array named 'list'.Now in the method 'numberOfRowsInSection' method it returns the list count...and in another method 'cellForRowAtInexPath' it displays data in cell...now when the view is pushed to the above view using pushViewnavigationcontroller,function is called in viewwillappear and data is shown in tableview..now when i come back to previous view and move to tableview view again...viewWillAppear method is called(i checked using NSLog)...but tableview displays the same previous data..that means methods numberOfRowsInSection,cellForRowAtIndexPath are called only Once at first click..why is it so??and what can be done to load the tableview again and again with changing values of the data..??
I found the solution by myself. Just use [tableview reloadData] in method viewWillAppear.

Resources