*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 件のコメント:
コメントを投稿