> > On 8 Mar 2012, at 16:58, Quincey Morris wrote: > >> >>> A more robust solution is a probably a separate -dispose method. >> >> Yes, though knowing when to call it can be a puzzle in itself, if there are >> multiple references to the object. In general, you'll probably need to >> invent a reference counting mechanism to keep track of when it's OK for your >> dispose method to actually dispose of things. That sounds ironic in a GC >> environment, but there's nothing wrong with reference counting when you need >> to keep count. :) >
It turns out that chasing my objects around trying to figure out when to safely dispose of their object resources is indeed a puzzle. I have implemented the following category to provide a reference counting mechanism for disposable resources. Using a category makes it easy to add this functionality to an existing class hierarchy. Some brief commentary and the code repo is at http://github.com/mugginsoft/MGSDisposable Regards Jonathan Mitchell Mugginsoft LLP #import <Foundation/Foundation.h> @interface NSObject (MGSDisposable) - (void)mgsMakeDisposable; - (BOOL)isMgsDisposable; - (NSUInteger)mgsDisposalCount; - (BOOL)isMgsDisposed; - (void)mgsRetainDisposable; - (void)mgsReleaseDisposable; - (void)mgsDispose; - (BOOL)isMgsDisposedWithLogIfTrue; - (void)mgsAssociateValue:(id)value withKey:(void *)key; - (void)mgsWeaklyAssociateValue:(id)value withKey:(void *)key; - (id)mgsAssociatedValueForKey:(void *)key; - (void)mgsLogSelector:(SEL)sel; @end #import "NSObject+MGSDisposable.h" #import <objc/runtime.h> // enable logging #define MGS_DISPOSAL_LOG // disable logging // comment the line below to enable logging #undef MGS_DISPOSAL_LOG static char mgsDisposableKey; NSString * const MGSAllowDisposalKey = @"MGSAllowDisposal"; NSString * const MGSAllowDisposaValue = @"Yes"; @implementation NSObject (MGSDisposable) /* - mgsMakeDisposable */ - (void)mgsMakeDisposable { #ifdef MGS_DISPOSAL_LOG [self mgsLogSelector:_cmd]; #endif // check if already disposable if ([self isMgsDisposable]) { return; } // assign an initial reference count of 1 NSNumber *refCount = [NSNumber numberWithUnsignedInteger:1]; [self mgsAssociateValue:refCount withKey:&mgsDisposableKey]; } /* - isMgsDisposable */ - (BOOL)isMgsDisposable { return ([self mgsDisposalCount] == NSUIntegerMax ? NO : YES); } /* - mgsDisposalCount */ - (NSUInteger)mgsDisposalCount { NSNumber *refCount = [self mgsAssociatedValueForKey:&mgsDisposableKey]; if (!refCount) { return NSUIntegerMax; } return [refCount unsignedIntegerValue]; } /* - isMgsDisposed */ - (BOOL)isMgsDisposed { NSUInteger refCount = [self mgsDisposalCount]; return (refCount == 0 ? YES : NO); } /* - mgsRetainDisposable */ - (void)mgsRetainDisposable { #ifdef MGS_DISPOSAL_LOG [self mgsLogSelector:_cmd]; #endif if (![self isMgsDisposable]) return; if ([self isMgsDisposed]) return; NSUInteger refCount = [self mgsDisposalCount]; if (refCount == NSUIntegerMax) { return; } [self mgsAssociateValue:[NSNumber numberWithUnsignedInteger:++refCount] withKey:&mgsDisposableKey]; } /* - mgsReleaseDisposable */ - (void)mgsReleaseDisposable { #ifdef MGS_DISPOSAL_LOG [self mgsLogSelector:_cmd]; #endif if (![self isMgsDisposable]) return; if ([self isMgsDisposed]) return; NSUInteger refCount = [self mgsDisposalCount]; if (refCount == NSUIntegerMax) { return; } // dispose when reference count == 1 if (refCount == 1) { [self mgsAssociateValue:MGSAllowDisposaValue withKey:MGSAllowDisposalKey]; [self mgsDispose]; } else { [self mgsAssociateValue:[NSNumber numberWithUnsignedInteger:--refCount] withKey:&mgsDisposableKey]; } } /* - mgsDispose */ - (void)mgsDispose { #ifdef MGS_DISPOSAL_LOG [self mgsLogSelector:_cmd]; #endif // we must be disposable if (![self isMgsDisposable]) return; // log and quit if already disposed if ([self isMgsDisposedWithLogIfTrue]) return; // disposal is only valid when the allow disposal key is found if (![self mgsAssociatedValueForKey:MGSAllowDisposalKey]) { NSLog(@"Disposal is not valid at this time."); return; } // mark this object as disposed [self mgsAssociateValue:[NSNumber numberWithUnsignedInteger:0] withKey:&mgsDisposableKey]; // remove the allow disposal key [self mgsAssociateValue:nil withKey:MGSAllowDisposalKey]; } /* - isMgsDisposedWithLogIfTrue */ - (BOOL)isMgsDisposedWithLogIfTrue { if (![self isMgsDisposable]) return NO; BOOL disposed = [self isMgsDisposed]; if (disposed) { NSLog(@"mgsDispose already called."); } return disposed; } /* - mgsAssociateValue */ - (void)mgsAssociateValue:(id)value withKey:(void *)key { objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN); } /* - mgsWeaklyAssociateValue */ - (void)mgsWeaklyAssociateValue:(id)value withKey:(void *)key { objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_ASSIGN); } /* - mgsAssociatedValueForKey */ - (id)mgsAssociatedValueForKey:(void *)key { return objc_getAssociatedObject(self, key); } /* - mgsLogSelector: */ - (void)mgsLogSelector:(SEL)sel { NSLog(@"%@ received %@.", self, NSStringFromSelector(sel)); } @end _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com