What is the most correct way for
ViewController B to access the
NSMutableArray that was filled by A?
I’d do something simple and only return to the decision if it causes problems. Something simple could be exposing the array in the public interface of controller A and sending notifications about the array updates so that B can watch:
@interface A
@property(readonly) NSArray *foos;
@implementation
- (void) updateFoos {
NSString *const key = @"foos";
[self willChangeValueForKey:key];
[foos doSomething];
[self didChangeValueForKey:key];
}
@interface B
@implementation
- (void) afterSettingA {
[a addObserver:self forKeyPath:@"foos" options:0 context:NULL];
}
- (void) observeValueForKeyPath: (NSString*) keyPath ofObject: (id) object
change: (NSDictionary*) change context: (void*) context
{
NSAssert([keyPath isEqualToString:@"foos"], @"Somethind fishy going on.");
// update what depends on foos
}
Another simple solution would be turning the array into a full-fledged model class that you would connect both to A and B. (The connection would have to be done outside the controllers to avoid excessive coupling. You can use Interface Builder, a ‘factory’ class that would wire the objects together or anything else that fits.)
@interface Foo
@property(readonly) NSArray* items;
@implementation
- (void) updateItems {
// send KVO notifications just as above
}
@interface A
@property(retain) Foo *fooModel;
@interface B
@property(retain) Foo *fooModel;
@interface Factory
@implementation
- (void) wireObjects {
A *a = [[A alloc] init];
B *b = [[B alloc] init];
Foo *fooModel = [[Foo alloc] init];
[a setFooModel:fooModel];
[b setFooModel:fooModel];
// Of course the A and B would be member variables of this
// class or you would return a root of the constructed object
// graph from this method, otherwise it would not make sense.
}
In the first solution the B controller has to have a pointer to A so that it can subscribe to the KVO notifications. This connection between the controllers is best maintaned somewhere else than in their code, ie. B should not create an instance of A. (This way you would introduce a tight coupling between A and B. Not very testable etc.) If you already instantiate the controllers in Interface Builder, this is the perfect place to give B the pointer to A. (Simply create an IBOutlet
for A in B.)
The second solution with the separate model class is a “cleaner” MVC and does not require the controllers to know each other – they both depend on the model class. You can instantiate the model and link it to the controllers in Interface Builder, too.
By the way: If B wants to watch for changes in some property of A, it has to subscribe after the link to A has been set. A simple but slighly wrong way to do it is to subscribe in the viewDidLoad
method of B. It’s convenient, but if the link to A gets changed after that, the notifications do not change accordingly. The harder but correct way to subscribe is in the setter for A – when somebody sets a new A, you cancel the notification subscriptions to the old A and subscribe to the new ones.