Early warning - code sample a little long...
I have a singleton NSMutableArray that can be accessed from anywhere within my application. I want to be able to reference the NSMutableArray
from multiple NIB files but bind to UI elements via NSArrayController
objects. Initial creation is not a problem. I can reference the singleton NSMutableArray
when the NIB gets loaded and everything appears fine.
However, changing the NSMutableArray
by adding or removing objects does not kick off KVO to update the NSArrayController
instances. I realize that "changing behind the controller's back" is considered a no-go part of Cocoa-land, but I don't see any other way of programmatically updating the NSMutableArray
and letting every NSArrayController
be notified (except it doesn't work of course...).
I have simplified classes below to explain.
Simplified singleton class header:
@interface MyGlobals : NSObject {
NSMutableArray * globalArray;
}
@property (nonatomic, retain) NSMutableArray * globalArray;
Simplified singleton method:
static MyGlobals *sharedMyGlobals = nil;
@implementation MyGlobals
@synthesize globalArray;
+(MyGlobals*)sharedDataManager {
@synchronized(self) {
if (sharedMyGlobals == nil)
[[[self alloc] init] autorelease];
}
return sharedMyGlobals;
}
-(id) init {
if(self = [super init]) {
self.globals = [[NSMutableArray alloc] init];
}
return self
}
// ---- allocWithZone, copyWithZone etc clipped from example ----
In this simplified example the header and model for objects in the array:
Header file:
@interface MyModel : NSObject {
NSInteger myId;
NSString * myName;
}
@property (readwrite) NSInteger myId;
@property (readwrite, copy) NSString * myName;
-(id)initWithObjectId:(NSInteger)newId objectName:(NSString *)newName;
@end
Method file:
@implementation MyModel
@synthesize myId;
@synthesize myName;
-(id)init {
[super init];
myName = @"New Object Name";
myId = 0;
return self;
}
@end
Now imagine two NIB files with appropriate NSArrayController
instances. We'll call them myArrayControllerInNibOne
and myArrayControllerInNib2
. Each array controller in the init
of the NIB controller sets the content of the array:
// In NIB one init
[myArrayControllerInNibOne setContent: [[MyGlobals sharedMyGlobals].globalArray];
// In NIB two init
[myArrayControllerInNibTwo setContent: [[MyGlobals sharedMyGlobals].globalArray];
When each NIB initializes the NSArrayController
binds correctly to the shared array and I can see the array content in the UI as you would expect. I have a separate background thread that updates the global array when content changes based on an external event. When objects need to be added in this background thread, I simply add them to the array as follows:
[[[MyGlobals sharedMyGlobals].globalArray] addObject:theNewObject];
This is where things fall apart. I can't call a willChangeValueForKey
and didChangeValueForKey
on the global array because the shared instance doesn't have a key value (should I be adding this in the singleton class?)
I could fire off an NSNotification
and catch that in the NIB controller and either do a [myArrayControllerInNibOne rearrangeObjects]
; or set the content to nil
and reassign the content to the array - but both of these seems like hacks and. moreover, setting the NSArrayController
to nil
and then back to the global array causes a visual flash within the UI as the content is cleared and re-populated.
I know I could add directly to the NSArrayController
and the array gets updated, but I don't see a) how the other NSArrayController
instances would be updated and b) I don't want to tie my background thread class explicitly to a NIB instance (nor should I have to).
I think the correct approach is to either fire off the KVO notification somehow around the addObject
in the background thread, or add something to the object that is being stored in the global array. But I'm at a loss.
As a point of note I am NOT using Core Data.
Any help or assistance would be very much appreciated.