Re: ivar access during -finalize
Re: ivar access during -finalize
>
> 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 (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