Re: Best design pattern for un-archiving instances of shared resources?

  • Thanks to all for the suggestions on and off list. I decided to go
    with David's suggestions and feel a little more Cocoa-fu now.

    Barry

    On 10/18/07, David Spooner <dave...> wrote:
    >
    > Barry,
    >
    > If I understand correctly, the dilemma is: you want to encode channel
    > objects directly as a simple means of saving their state, but you want
    > channel objects to be unique and to not have 'legacy' channel objects as a
    > result of unarchiving...
    >
    >
    >
    > On 18-Oct-07, at 6:06 PM, Barry Wark wrote:
    >
    > The Controller creates instances of Channels at startup but then wants
    > to replace these instances with the unarchived instances that contain
    > the users' settings. I can't skip the creation of the Channel
    > instances at startup because there may be channels that are now
    > available that weren't before (because of hardware configuration
    > changes in the A-D converter) and to simply replace the Controller's
    > Channels with the unarchived set would overwrite these new channels
    > and/or add Channel instances that correspond to channels that are no
    > longer physically present.
    >
    > I suggest not encoding Channel objects directly.  Instead introduce a means
    > of specifying the properties of each class of channel that you want to
    > encode...
    >
    >
    > @interface Channel(...)
    >
    > - (NSArray *) channelPropertyKeys;
    >
    > @end
    >
    >
    >
    > Then add an auxiliary class to simplify archiving those properties...
    >
    >
    > @implementation ChannelSettings : NSObject
    > {
    > id identifier;
    > NSDictionary *properties;
    > }
    >
    > - (id) initWithChannel:(Channel *)channel
    > {
    > identifier = [[channel channelId] retain];
    > properties = [[NSMutableDictionary alloc] init];
    >
    > NSEnumerator *enumerator = [[channel channelPropertyKeys]
    > objectEnumerator];
    > for (NSString *key; (key = [enumerator nextObject]) != nil; )
    > [properties setObject:[channel valueForKey:key] forKey:key];
    >
    > return self;
    > }
    >
    >
    > - (id) identifier
    > ...
    >
    > - (NSDictionary *) properties
    > ...
    >
    > // NSCoding...
    >
    > @end
    >
    >
    >
    > Create your set of channel objects with default properties upon
    > initialization, then assign the saved properties stored in the decoded
    > channel settings objects ...
    >
    >
    > NSEnumerator *enumerator = [decodedSetOfChannelSettings
    > objectEnumerator];
    > for (ChannelSetting *setting; (setting = [enumerator nextObject]) !=
    > nil; )
    > [[Channel channelWithIdentifier:[setting identifier]]
    > setValuesForKeysWithDictionary:[setting properties]];
    >
    >
    > (where +channelWithIdentifier: returns nil for non-current identifiers, of
    > course)
    >
    >
    > Finally, other objects in the program (such as the objects that
    > coordinate stimulus presentation and data recording) archive
    > references to the Channel instances when they save their settings.
    > When unarchived, obviously these objects want references to the
    > Channel instances being managed by the Controller, not the orphaned
    > instance that gets unarchived.
    >
    > I would then encode references to channels using a proxy class...
    >
    >
    > @implementation ChannelReference : NSObject
    > { id identifier; }
    >
    > - (id) initWithChannel:(Channel *)aChannel
    > {
    > identifier = [[channel identifier] copy];
    > return self;
    > }
    >
    > - (void) dealloc
    > {
    > [identifier release];
    > [super dealloc];
    > }
    >
    > - (id) initWithCoder:(NSCoder *)coder
    > {
    > identifier = [[coder decodeObject] retain];
    > return self;
    > }
    >
    > - (void) encodeWithCoder:(NSCoder *)coder
    > {
    > [coder encodeObject:identifier];
    > }
    >
    > - (id) awakeAfterUsingCoder:(NSCoder *)coder
    > {
    > [self autorelease];
    > return [[Channel channelWithIdentifier:identifier]
    > retain];
    > }
    > @end
    >
    > and
    >
    > @implementation Channel(...)
    >
    > - (id) replacementObjectForCoder:(NSCoder *)coder
    > { return [[[ChannelReference alloc] initWithChannel:self]
    > autorelease]; }
    >
    > @end
    >
    >
    >
    > - (id)initWithCoder:(NSKeyedUnarchiver*)decoder {
    >
    > if(self = [super init]) { //super-class doesn't implement initWithCoder
    > int channelNumber = [decoder
    > decodeIntForKey:@"channelNumber"];
    > id existingChannel;
    > if(existingChannel = [[Controller sharedInstance]
    > channelWithChannelNumber:channelNumber]) { //channel
    > already exists
    > [self release];
    > self = existingChannel;
    > }
    >
    > if(![self isInitialized]) {
    > ...decode and set user-configured properties
    > [self setInitialized:YES];
    > }
    > }
    >
    > return self;
    > }
    >
    > with an init method that looks like
    >
    > - (id)init {
    > if(self = [super init]) {
    > ...set default values for user-configured properties
    > [self setInitialized:NO];
    > }
    > }
    >
    > I would avoid complicating the initialization and decoding methods...
    >
    > Cheers,
    > dave
    >
    >
previous month october 2007 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Go to today