views:

397

answers:

5

I'm looking for a design pattern to manage a pool of objects. A thread can request an object, in my case an object representing a remote host.

If the host-object does not exist it is created, added to the pool and a reference returned.

If another thread or object requests the same (remote host) object it too is given the reference. If a different remote host is requested it is created and added to the 'pool' of objects.

Once the host-object is no longer needed by any thread or object is removes it self form the pool.

  1. Is there pattern for this, and what is it name?
  2. I will be implementing this in Objective-C, any specific help would be useful.
A: 

1.What about simple 'Object Pool' ?

2.See there I'm not objective-c devlopper, though

An object pool implies a homogenous set of objects that are borrowed and returned, which isn't the same as what rjstelling is describing (which is more like a factory for singletons).
smorgan
Actually, what rjstelling describing is a uniquing table, similar to what CoreData uses to ensure that no more than one object referring to a particular data model instance will be resident in memory at any one time.
Jim Dovey
A: 

I think, the best solution here is Multiton (it's like Map with 1 to 1 mapping, http://en.wikipedia.org/wiki/Multiton_pattern).

It'll have 2 methods - getHost (String hostIdentifier) and returnHost (String hostIdentifier).

For every host it should have a counter. If counter == 0 and method getHost is called then you should create new instance of required host and increse counter.

If counter == 1 and returnHost is called then you should delete host instance and decrease counter.

You should also think about thread safety.

Roman
+1  A: 

I'd usually implement this kind of pattern with something like a + (id)hostWithIdentifier:(id)someIdentifier; method (substitute better types where known), which uses an NSMapTable to keep track of the items with weak references. If an instance isn't found for the given identifier, it allocates it and puts it in the table.

Note that if you're not using GC, you'll need to clear out the entry in the NSMapTable in your host object's -dealloc method.

Also note, as somebody else said, that you'll want to be careful if this is multithreaded code. Use some kind of locking mechanism.

bdrister
A: 

It sounds like each individual host object is a Singleton pattern. Could you just keep a collection of the individual Singletons?

dsrekab
+1  A: 

An alternative to using NSMapTable (which is unavailable on the iPhone, for instance) is to use the CoreFoundation equivalents. Those can contain regular non-retained pointers. You still need to pull things out of that list however, but that can be done easily in an object's -dealloc method.

One possibility is using a CFSet. This would require that your objects all respond to -isEqual: and -hash properly, so that the set can determine if an object is already present accurately.

For instance (note that I've not compiled this but it ought to be at least 90% correct):

static id __uniqueObjects = nil;    // GC uses NSHashTable, non-GC uses NSSet (inited using CFSet APIs)

@interface MyHost : NSObject
{
    NSURL * _hostURL;
}

@property (nonatomic, readonly) NSURL * hostURL;

+ (MyHost *) uniqueHost: (MyHost *) aHost;
+ (void) removeUniqueHost: (MyHost *) aHost;     // only used in non-GC environment

- (id) initWithURL: (NSURL *) hostURL;

@end

@implementation MyHost

- (id) initWithURL: (NSURL *) hostURL
{
    self = [super init];
    if ( self == nil )
        return ( nil );

    // use copy, because NSURL implements NSCopying protocol
    _hostURL = [hostURL copy];

    // if there's a version around already, release this instance
    MyHost * result = [[self class] uniqueHost: self];
    if ( result != self )
    {
        // set _hostURL to nil so we don't inadvertently remove anything from uniqueing table in -dealloc
        [_hostURL release];
        _hostURL = nil;
        [self release];
    }

    // return either self or a pre-existing unique instance
    return ( result );
}

- (void) dealloc
{
    // non-GC objects need to explicitly remove themselves from the uniqueing table
    [[self class] removeUniqueHost: self];
    [_hostURL release];
    [super dealloc];
}

// no need for -finalize -- under GC we use NSHashTable with weak references

- (NSUInteger) hash
{
    return ( [_hostURL hash] );
}

- (BOOL) isEqual: (MyHost *) other
{
    if ( other == self )
        return ( YES );

    return ( [_hostURL isEqual: other->_hostURL] );
}

+ (MyHost *) uniqueHost: (MyHost *) aHost
{
    if ( __uniqueObjects == nil )
    {
        // use low-level routine to check, because iPhone has no NSGarbageCollector class
        // we use NSHashTable or NSMutableSet, because they both respond to the same messages (NSHashTable is modeled on NSMutableSet)
        if ( objc_collectingEnabled() )
        {
            // hash table has zeroing weak memory, object equality (uses -isEqual:), and does NOT copy objects, just retains them
            __uniqueObjects = [[NSHashTable hashTableWithWeakObjects] retain];
        }
        else
        {
            CFSetCallBacks cb = {
                0,                  // version
                NULL,               // retain (non-retaining references)
                NULL,               // release (non-retaining references)
                CFCopyDescription,  // CF (plain C function) equivalent for -description
                CFEqual,            // CF (plain C function) equivalent for -isEqual:
                CFHash              // CF (plain C function) equivalent for -hash
            }
            __uniqueObjects = (NSMutableSet *) CFSetCreateMutable( kCFAllocatorDefault, 0, &cb );
        }
    }

    MyHost * result = nil;
    @synchronized(__uniqueObjects)
    {
        // treat both like an NSMutableSet & we're golden
        // we use the -member: function to get an existing matching object from the collection
        result = [[__uniqueObjects member: aHost] retain];
        if ( result == nil )
        {
            // store & return the passed-in host object
            [__uniqueObjects addObject: aHost];
            result = aHost;
        }
    }

    return ( result );
}

+ (void) removeUniqueHost: (MyHost *) aHost
{
    if ( __uniqueObjects == nil )
        return;     // shouldn't ever happen, but it's a good idea to check at least

    @synchronized(__uniqueObjects)
    {
        [__uniqueObjects removeObject: aHost];
    }
}

@end

Hopefully I've put enough in here to answer any questions/thoughts you might have about this design pattern.

Jim Dovey