Friday 9 November 2012

Key Value Observing iOS / KVO


You can observe changes across different objects/classes. I think the problem is in the options parameter of addObserver:forKeyPath:options:context:.
There are various options for the type of observing you want to do. The KVO Guide is a good starting point, but you probably want NSKeyValueObservingOptionNew, which I use in the example below.
First, "name" should be a public property in ClassB.
Second, you probably don't need to add the observer to "b" in viewWillAppear in ClassA, because you don't need to add it everytime the ClassA view is going to appear. You just need to add the observer once, when you create the ClassB view. Once you've added the observer, theobserveValueForKeyPath:ofObject:change:context: method will be executed in ClassA, so you can do the update to the ClassA UI from there. You shouldn't need to do anything every time ClassA is about to appear.
In Class A, you should probably create ClassB just before you are going to push ClassB onto the controller stack, presumably in the event handler for some action the user took. Immediately after you create ClassB, add the observer in ClassA with the correct NSKeyValueObservingOption value.
If you just want to be notified whenever the public property "name" in ClassB is changed, then try this:
ClassB
@interface ClassB : UIViewController {
}

@property (retain) NSString* name;

- (void) aMethodThatModifiesName:(NSString*)newName;

@end


@implementation ClassB 

@synthesize name;

- (void) aMethodThatModifiesName:(NSString*)newName {
    // do some stuff
    self.name = newName;
}

@end
ClassA
@interface ClassA : UIViewController {
}

@property (nonatomic, retain) IBOutlet UILabel* nameLabel;

- (IBAction) someEventHandler:(id)sender;

@end

@implementation ClassA

- (IBAction) someEventHandler:(id)sender {
    ClassB* b = [[ClassB alloc]init];
    [b addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
    [self.navigationController pushViewController:b animated:YES];
    [b release];
}

- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
    if ([keyPath isEqual:@"name"]) {
        NSString* changedName = [change objectForKey:NSKeyValueChangeNewKey];
        // do something with the changedName - call a method or update the UI here
        self.nameLabel.text = changedName;
    }
}

@end

Difference between Notification and Delegate or Delegation / Notification


Is it generally considered best practise to reduce the coupling or dependencies between classes in your application. A key benefit is that your code becomes easier to maintain since a change in one class is less likely to impact another dependent class. It also becomes easier to reuse classes across applications.
Cocoa implements a number of features such as delegates and notifications that make this easier to achieve though it can be confusing at first to know when you should use one or the other (or both) or these techniques.

Delegation

The concept of delegation as its name suggests allows an object to send a message to another object (the delegate) so that it can customise the handling of an event. The UITableViewController provides a common example of this in an iPhone application. The delegate of the UITableViewController is informed when a user will do something such as select a row allowing the delegate to prevent or change the action. A further call is made to the delegate when the user did do something allowing the delegate to take further action such as saving data or triggering the update of a view.
The important point about delegation is the way it is implemented allows for minimal dependency between the delegating object and its delegate. The delegating object needs to have a reference to its delegate so that it can call methods in the delegate. However this reference is declared using the anonymous id type:
id delegate;
This way the delegating object does not need to know the type of its delegate which removes a big potential dependency between the classes. This weak reference between the two classes means that the compiler cannot verify that a delegate class actually implements the methods expected by the delegating object.
To avoid unexpected runtime exceptions a delegating object can test if a delegate responds to a certain message before invoking it:
if ([delegate respondsToSelector:@selector(myMethod)]) {
  [delegate myMethod];
}
This allows a delegate to optionally implement some methods. The ability to specify some delegate methods as mandatory or optional can be formally defined (and checked at compile time) through the use of protocols. There is an example on using delegates with protocols in the post on iPad Modal View Controllers so I will skip ahead to the sending and receiving of notifications.

Notification

The concept of notification differs from delegation in that it allows a message to be sent to more than one object. It is more like a broadcast rather than a straight communication between two objects. It removes dependencies between the sending and receiving object(s) by using a notification center to manage the sending and receiving of notifications. The sender does not need to know if there are any receivers registered with the notification center. There can be one, many or even no receivers of the notification registered with the notification center.
The other difference between notifications and delegates is that there is no possibility for the receiver of a notification to return a value to the sender. Remember the earlier example of a delegate method indicating that a user will select a row in a table. The delegate has the opportunity to return a value which prevents the user from selecting a row. If we use a notification to inform another object that the user is selecting a row there is no way to return a value to influence this action.
Typical uses of notifications might be to allow different objects with an application to be informed of an event such as a file download completing or a user changing an application preference. The receiver of the notification might then perform additional actions such as processing the downloaded file or updating the display.

The Default Notification Center

Cocoa allows you to create multiple notification centers but in practise on the iPhone you will probably only ever need to use the default notification center. You access this default notification center using a class method as follows:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

Naming a Notification

A notification consists of a name, an object and an optional dictionary to contain user information. The name is used to identify the notification so you should try to choose something unique to your application. Using a two or three letter prefix specific to your application is a good way to do this. Apple also recommends that you define the name as a global constant string variable rather than using the hard coded string value in your code. If you look at the way Apple defines notification names you will also see the name often follows the pattern