Design Pattern Reference: Unique Object Pool
I am looking for a design pattern for object pool management. A thread can request an object, in my case an object representing the remote host.
If the host object does not exist, it is created, added to the pool, and a reference is returned.
If another thread or object requests the same (remote host) object, it is also assigned a link. If another remote host is requested, it is created and added to the object "pool".
As soon as the host object is no longer needed by any thread or object, it removes it itself from the pool.
- Is there a pattern for this and what is the name?
- I will be implementing this in Objective-C, any specific help would be helpful.
a source to share
An alternative to using NSMapTable (not available on iPhone, for example) is to use CoreFoundation equivalents. They can contain regular non-retained pointers. However, you still need to get things out of that list, but it's easy to do in the object -dealloc method.
One of the possibilities is using CFSet. This would require your objects to respond to -isEqual:
and -hash
correctly, so that the collection can determine if an object is already present for sure.
For example (note that I didn't compile this, but should 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 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 a good idea to check at least
@synchronized(__uniqueObjects)
{
[__uniqueObjects removeObject: aHost];
}
}
@end
Hopefully I've put enough here to answer any questions / thoughts you might have regarding this design pattern.
a source to share
I think the best solution here is Multiton (this is similar to Map with 1 to 1 mapping, http://en.wikipedia.org/wiki/Multiton_pattern ).
It will have 2 methods - getHost (String hostIdentifier) ββand returnHost (String hostIdentifier).
For each host, it must have a counter. If counter == 0 and getHost is called, then you must create a new instance of the required host counter and increse.
If counter == 1 and returnHost are called, you must delete the host instance and decrement the counter.
You also need to think about thread safety.
a source to share
I usually use a pattern like this with something like + (id) hostWithIdentifier: (id) someIdentifier; a method (override more well-known types if known) that uses NSMapTable to keep track of weakly referenced elements. If an instance is not found for the given identifier, it extracts it and places it in the table.
Note that if you are not using GC, you need to clear the NSMapTable entry in your object's -dealloc method.
Also note, as someone else said, that you need to be careful if this is multi-threaded code. Use some kind of locking mechanism.
a source to share
1. What about a simple "Object Pool" ?
2. See there I am not an objective-c devlopper though
a source to share