cockscomblog?

cockscomb on hatena blog

モバイルアプリのObserverパターン

ReactiveCocoa勉強会関西にてObserverパターンについてお話ししましたので、以下にその内容をまとめます。


Observerパターンは、GoFの23のデザインパターンのうちの一つで、モデルが状態の変化をしビューに通知するパターンです。GUIアプリケーションの開発で多用されます。もちろんスマートフォンアプリの開発においても大変役に立つので、いくつかの例を挙げて見ていきます。

Objective-CのKey-Value Observing

static void * Context = &Context;


- (void)anything
{
    [object addObserver:self
             forKeyPath:NSStringFromSelector(@selector(property))
                options:NSKeyValueObservingOptionNew
                context:Context];
}


- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (context == Context) {
        doSomething();
    }
}

上記は監視する側の例です。NSKeyValueObservingプロトコルaddObserver:forKeyPath:options:contextを呼び出し、変化があったときobserveValueForKeyPath:ofObject:change:context:が呼ばれるというものです。NSStringFromSelector()contextを利用するのは良い習慣です。

self.property = object;
NSMutableArray *arrayProxy =
    [array mutableArrayValueForKeyPath:
        NSStringFromSelector(@selector(property))];
[proxy addObject:object];

KVOの通知を発火させるためには、Key-Value Codingに準拠するようにプロパティへの代入を行うか、あるいはコレクションのKVOを発火させるためにmutableArrayValueForKeyPath:が返すプロキシオブジェクトを利用します。

このようにObjective-Cでは、KVOという仕組みによってObserverパターンを簡単に利用できるようになっています。

AndroidJavaのObserverパターン

AndroidにおけるObserverパターンの実現ためには、素朴にObserverパターンを実装するほか、java.beansパッケージのPropertyChangeSupportクラスを利用する方法があります。

public class Bean {
    private final PropertyChangeSupport mChanges;

    private String mName;


    public Bean() {
        mChanges = new PropertyChangeSupport(this);
    }


    public void addListener(PropertyChangeListener listener) {
        mChanges.addPropertyChangeListener(listener);
    }


    public void deleteListener(PropertyChangeListener listener) {
        mChanges.removePropertyChangeListener(listener);
    }


    public String getName() { return mName; }


    public void setName(String name) {
        String oldName = mName;
        mName = name;
        mChanges.firePropertyChange("name", oldName, mName);
    }

}

PropertyChangeSupportインスタンスを保持し、PropertyChangeListenerインターフェースのオブジェクトを受け付けます。そして監視させたいフィールドの変更があったとき、firePropertyChange()を呼び出すことで、PropertyChangeListenerPropertyChangeEventが送られます。

java.beansパッケージの中で、このPropertyChangeSupport関連の機能だけがAndroidにも存在しています。KVOと似たような形で利用できるかもしれません。

またjava.util.Observableを継承するという方法もありますが、あまり使い勝手が良くないかもしれません。

Swift

Swiftについてはどうでしょうか。SwiftからもNSObjectのKVOは利用できます。しかし純粋なSwiftのオブジェクトについては、これまでのKVOは利用できません。

Appleのドキュメント“Using Swift with Cocoa and Objective-C”の“Adopting Cocoa Design Patterns”を参照すると、今日現在で

Information forthcoming.

となっています。正式なリリースまでに何か明らかになるとよいのですが。

ここからは与太話ですが、もしSwiftにKVOライクな仕組みが導入されるとしたら、Objective-Cのようなランタイムで動的に何かするのではなく、コンパイルタイムで解決できるような仕掛けになるのではないでしょうか。そのほうがSwift的で、より安全かつ最適化しやすいようにも思います。

以上となりますが、今後もライフワークとしてObserverパターンについて考えていきたいと思います。