読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

さくっと基本を振り返る4

Objective-C 日記

ポリモーフィズム多態性)について考えてみる。
Java で言えばインターフェース実装、もしくは、メソッドオーバライド、C++ で言えば仮想関数とか。
まぁ早い話が大本の枠を作って、実装は個々のクラス次第ということ。

public interface IWorker
{
    void Work();
}

public class Programmer : IWorker
{
    public void Work()
    {
        /* コード書いたり */
    }
}

public class Teacher : IWorker
{
    public void Work()
    {
        /* 他人に何か教えるとか */
    }
}

とまぁこんな感じにしといて、どっちも「Worker」だから、

Worker worker = getRandomJob();
worker.Work();

何の職業かは知らなくても、働けって言えば働いてくれる。どう働くかは実装次第という仕様。
Objective-C では、インターフェースは必要ない。だってみんな「Object」であって、メッセージを受けて動くか動かないかだけだから。

@interface Programmer {
}
- (void)work;
@end

@interface Teacher {
}
- (void)work;
@end

と、とにかく同じメソッドさえあるのなら

id worker = [self randomJob];
[worker work];

と呼べる。
前に述べた通り、型安全などないので、randomJob の返り値が、work メッセージレセプタ(便宜上メソッドを分けて呼称)を実装しているかどうかは問題ではないし、null(nil) であっても特に問題はない。*1

とはいえ、このような書き方で、実装してるかどうか相手任せで、実行時例外を出されても困る。そんなとき、3種類の手段がある。

  • protocol を作成、実装する。
  • 非形式プロトコルを作成する。
  • forwardInvocation で処理してしまう。

最初の方法は、Java/C++/C# のインターフェースと何ら変わりがない。なぜこのようなものがあるかというと、シリアライズされた際の転送速度を稼ぐとか、アクセス速度を稼ぐ狙いがある。
書くのが面倒だったり、必ず実装とかだるいので滅多に書かない。書くとするとこんな感じ。

@protocol Worker

- (void)work;

@end

@interface Programmer : NSObject <Worker> {
}

- (void)work;

@end

次に非形式プロトコルだが、これはもっと単純な実装になる。
何の事はない、共通の基底クラスにカテゴリを使ってメソッドを作ってしまう。これは、実装を変更するかどうかを、継承先で任意にできる。

@interface NSObject(Worker)
- (void)work;
@end

NSObject を継承するすべてのオブジェクトは、work メソッドがある事が保証される。
最後は、invocationForwarding を使う方法だ。ただし、この方法は Cocoa ありきか、、、。

@interface Neet : NSObject
{
    id other;
}
@end

@implementation Neet

- (NSMethodSignaure*)methodSignatureForSelector:(SEL)selctor
{
    // other がメソッド持ってたら丸投げする。
    if ([other respondsToSelector:selector]) {
        return [other methodSignatureForSelector:selector];
    }
    
    return [super methodSignatureForSelector:selector];
}

- (void)forwardInvocation:(NSInvocation*)invocation
{
    // 問答無用で other に丸投げ。テラヒドス
    [invocation invokeWithTarget:other];
} 

という感じ。

*1:実装されていなければ例外が出るけど