*1 NSColor のメソッドに - (CGColorRef)CGColor が追加されました。10.8 以降 使用できます。
NSColor+CGColor.h
#import <Foundation/Foundation.h> @interface NSColor (CGColor) - (CGColorRef)CGColor; @end
NSColor+CGColor.m
#import "NSColor+CGColor.h" #import <objc/runtime.h> @interface NSColor (Swizzling) - (void)methodToExchangeImplementation; - (void)preDealloc; @end @interface NSColor (Accessor) - (void)initializeCGColorCollection; - (void)releaseColorCollection; - (void)setCGColorRef:(CGColorRef)colorRef; @end @implementation NSColor (Swizzling) static IMP PRE_DEALLOC_IMPLEMENTATION = NULL; static IMP DEALLOC_IMPLEMENTATION = NULL; - (void)methodToGetImplementation { // メソッド構造体を取得する Method preDealloc = class_getInstanceMethod([self class], @selector(preDealloc)); Method dealloc = class_getInstanceMethod([self class], @selector(dealloc)); // メソッド構造体からメソッド実装を取得する PRE_DEALLOC_IMPLEMENTATION = method_getImplementation(preDealloc); DEALLOC_IMPLEMENTATION = method_getImplementation(dealloc); } - (void)methodToExchangeImplementation { if (PRE_DEALLOC_IMPLEMENTATION == NULL || DEALLOC_IMPLEMENTATION == NULL) { [self methodToGetImplementation]; } // メソッド構造体を取得する Method preDealloc = class_getInstanceMethod([self class], @selector(preDealloc)); Method dealloc = class_getInstanceMethod([self class], @selector(dealloc)); // メソッドの実装を互いに入れかえる method_exchangeImplementations(preDealloc, dealloc); } - (void)preDealloc { // 辞書に CGColorRef を加えたオブジェクトは dealloc の前に解放する。 [self setCGColorRef:NULL]; [self releaseColorCollection]; // dealloc を実装から呼ぶ。 DEALLOC_IMPLEMENTATION(self, _cmd); } @end @implementation NSColor (Accessor) static NSMutableDictionary *CGColorCollection = nil; - (void)initializeCGColorCollection { @synchronized (self) { if (!CGColorCollection) { CGColorCollection = [[NSMutableDictionary dictionary] retain]; [self methodToExchangeImplementation]; } } } - (void)releaseColorCollection { @synchronized (self) { if ([CGColorCollection count] == 0) { [CGColorCollection release]; CGColorCollection = nil; [self methodToExchangeImplementation]; } } } - (void)setCGColorRef:(CGColorRef)newColorRef { NSString *aKey = [NSString stringWithFormat:@"%p",self]; @synchronized (self) { NSValue *value = [CGColorCollection valueForKey:aKey]; if (value) { CGColorRef colorRef = (CGColorRef )[value pointerValue]; [CGColorCollection removeObjectForKey:aKey]; CGColorRelease(colorRef); } if (newColorRef != NULL) { [CGColorCollection setValue:[NSValue valueWithPointer:newColorRef] forKey:aKey]; } } } @end @implementation NSColor (CGColor) - (CGColorRef)CGColor { [self initializeCGColorCollection]; // 現在のカラーの情報を取得する。 NSColorSpace *colorSpace = [self colorSpace]; size_t numberOfComponents = (size_t)[self numberOfComponents]; CGFloat *components = (CGFloat *)calloc(numberOfComponents, sizeof(CGFloat)); [self getComponents:components]; // CGColorRef を生成する。 CGColorRef colorRef = CGColorCreate([colorSpace CGColorSpace], components); free(components); // 自身をキーに生成した CGColorRef を辞書に加える。 [self setCGColorRef:colorRef]; return colorRef; } @end
こう考えてみた
- 単に NSColor からCGColor オブジェクトのインスタンスを生成するのでなく、NSColor の寿命に応じて 生成した CGColor の寿命もシンクロさせる。
- カテゴリはインスタンス変数の追加が出来ないので CGColor オブジェクトを適当なスコープ内に定義したグローバル変数で管理する。
preDealloc で擬似的なクラス変数に見立てた CGColorCollection を管理してやります。CGColorCollection が CGColor を持たなければメソッドの実装を元に戻してやります。ランタイムは NSColor の dealloc だと思って preDealloc を呼出しているので、最終的に本来呼ばれるべき dealloc を実装から直接呼んでやります。
0 件のコメント:
コメントを投稿