Re: Best design pattern for un-archiving instances of shared resources?
Re: Best design pattern for un-archiving instances of shared resources?
- Subject: Re: Best design pattern for un-archiving instances of shared resources?
- From: "Barry Wark" <email@hidden>
- Date: Sat, 20 Oct 2007 01:26:26 -0700
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 <email@hidden> 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
>
>
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden