光学薄膜設計ソフトの設計 その19 - 変更通知の実装 [考え中 - 光学薄膜設計]
最近コードを書いていなくて、勘をなくさないようにするために、ほったらかしにしてあった光学薄膜設計ソフトの実装を始める。今日は具体的なコード。
変更通知の実装
前回考えた変更通知のメカニズムのための抽象クラスを実装してみる。
まず主変数のクラス
@protocol OTFPrimer - (void)addDependant:(id)obj; - (void)removeDependant:(id)obj; - (void)notify; @end @interface OTFPrimer : NSObject <OTFPrimer> { NSMutableArray *dependants; } @endと従変数のクラス
@protocol OTFDependant - (void)setRevisedBy:(id)obj; @end @interface OTFSuccessor : OTFPrimer <OTFDependant> { BOOL isRevised; } - (void)resetRevised; - (BOOL)isRevised; @endOTFPrimerというのが主変数のクラスで、dependantsというNSMutableArrayをインスタンス変数に持ち、そこに登録、削除するためのメソッドと通知のためのメソッドを持っている。この3つのメソッドはプロトコルにしてある。
OTFSuccessorは従変数のクラスで、OTFPrimerを親クラスに持っている。さらにisRevisedというインスタンス変数を持っていてsetRevisedBy:というメソッドを実装している。このメソッドはOTFPrimerのnotifyメソッドが呼ばれたとき、dependantsの要素全部に送られる。このメソッドもプロトコルにしてある。また、このプロトコルにはついでに通知を受ける側から登録削除するためのユーティリティメソッドを書いてある。
OTFPrimerのimplementationは
@implementation OTFPrimer - (id)init { self = [super init]; dependants = [[NSMutableArray alloc] initWithCapacity:0]; return self; } - (void)dealloc { [dependants release]; [super dealloc]; } - (void)addDependant:(id)obj { if ([[obj class] conformsToProtocol:@protocol(OTFDependant)]) [dependants addObject:obj]; } - (void)removeDependant:(id)obj { [dependants removeObject:obj]; } - (void)notify { [dependants makeObjectsPerformSelector:@selector(setRevisedBy:) withObject:self]; } @endみたいな簡単なものである。 initメソッドでdependantsを確保して、addDependant:では渡されたオブジェクトがsetRevisedBy:に応答することを確認してからdependantsに追加している。そしてnotifyメソッドでは要素にsetRevisedBy:メソッドを投げている。makeObjectsPerformSelector:withObject:というNSArrayのメソッドは要素全部に同じメソッドを投げるためのもの。たとえば
[array makeObjectsPerformSelector:@selector(aMethodWith:)withObject:obj];とすると、arrayの要素オブジェクトelementに
[element aMethodWith:obj];というメソッドが投げられる。このメソッドは
- (void)aMethodWith:(id)anObj;というシグネチャの形でなければならない。引数をとらないメソッド用の makeObjectsPerformSelector:もある。
OTFSuccessorは
@implementation OTFSuccessor - (id)init { self = [super init]; isRevised = YES; return self; } - (void)setRevisedBy:(id)obj { isRevised = YES; [self notify]; } - (BOOL)isRevised { return isRevised; } - (void)resetRevised { isRevised = NO; } @endというようなものである。
setRevisedBy:が呼ばれるとisRevisedをセットして、自分を監視しているオブジェクトに通知するだけである。これでOTFPrimerが自分のnotifyを呼ぶと、自分を監視してるOTFSuccessorのsetRevisedBy:が呼ばれ、そのOTFSuccessorを監視してるOTFSuccessorのsetRevisedBy:が呼ばれる。
実際の値を確保するクラスはこれを継承して、値が変わったときに自分のnotifyを呼べばよい。この実装の問題点
実はこれはあまりに簡単すぎて問題がひとつある。それは通知のループを排除できていない、ということである。例えばA→B→A、あるいはもっと簡単にA→Aという通知経路が作れてしまう。この場合、通知がひとつあると無限ループに陥る。また、NSMutableArrayは要素にretainを送るので、A→B→Aを作ってしまうとそのままではretain loopになる、ということになる。
経路作成のときのループの検出は難しいけど、例えば通知に発生させたオブジェクトを記録して同じ発生場所なら通知を伝播しない、ということにすれば無限ループに陥ることはないし、BOOL変数であるisRevisedも変更は1回だけになる。
今回の場合、それほど深い構造ではないのと、計算のアルゴリズム上、無限ループにはならないことが保証できるので、このままいくことにする。
もしこれを一般的な通知問題に流用するときは注意が必要である。
つぎは、値を確保する実体クラスの実装を考える。
コメント 0