プロキシ(NSProxy)による変更通知 (その1)

さて、日が空いてしまったが先日の続き。

プロキシの同メソッドをオーバライドすることでターゲットオブジェクトの処理の前後に処理を挿入することが出来る。

DynamicProxyでターゲットオブジェクトの全てのメッセージに割り込みたかったのは、今までJavaC#で同様に実現してきた「オブジェクトの変更の検知とその通知」を自動的に行うことだ。

変更の検知

オブジェクトのプロパティの変更を検知することが出来れば、それを捕捉して通知を行うことができる。 プロパティの変更を検知するにはJavaであればJavaBeasn規約に沿ったアクセッサ(セッター)の実行、C#であれば同様に実行された内部メソッド"set_"の実行を捕捉して、値が変更されていることを確認することが出来れば良い訳だ。

Objective-Cの場合KVC準拠で@synthesizeで合成されたアクセッサが実行されたのを検知するのが良いだろう。単純な単項目のプロパティであれば、プロパティ名のサフィクスに"set"を付加してプロパティ名の先頭一桁をキャピタライズしたのが変更に使用される(はず)のメソッドだ。

#import <Foundation/Foundation.h>

@interface ChangeNotifyDto : NSObject

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *address;

@end

@implementation AssetDto

@synthesize name;
@synthesize address;

@end

このようなクラスの場合、synthesizeで合成されるプロパティ"name"のセッターは"setName"、"address"のセッターは"setAddress"となるはずであり、これを捕捉してやればよいはずだ。

先日紹介したDynamicProxyを継承したNotifyChangeProxyというプロキシクラスを用意して、-forwardInvocation:メソッドをオーバライドして実装してみる。

NotifyChangeProxy-forwardInvocation:
- (void)forwardInvocation:(NSInvocation *)invocation
{
    if ( targetObject )
    {   
        NSString *methodName = NSStringFromSelector([invocation selector]);
        
        if ([methodName hasPrefix:@"set"]) //セッターか?
        {
            //メソッド名からプロパティ名を取得
            int len = [methodName length]-3;
            NSString *propertyName = [methodName substringWithRange:NSMakeRange(3, len-1)]

            //この後やること
            
            //プロパティ名から現在のプロパティ値取得
            //メソッドのパラメタから新たに設定されるはずのプロパティ値を取得
            
            //新旧のプロパティを比較して変更されていれば通知
            
        }
        
        [invocation setTarget:targetObject];
        [invocation invoke];
    }
}

メソッド名はNSInvocationクラスから取得できるセレクタから取得できる。
NSStringクラスは無いだろうと思っていた"hasPrefix:"、"hasSuffix:"なんてメソッドもあった。C系にしては高機能なんだな。

次はコメントされた処理を埋めていこう。※

※私が今やろうとしていることはCocoaフレームワークでは他の方法を使って簡単に実装できることは知っているが、敢えてその方法は外して考えている。