クラス内から自身のクラスメソッドを呼び出すときの書き方について

小ネタ。

クラス内部のクラスメソッドを呼び出すときに、よく以下のような書き方を見る。

@interface MyObject : NSObject
@end

@implementation MyObject

+(void)func1 {
    NSLog(@"hello");
}

+(void)func2 {
    [MyObject func1];
}

-(void)method1 {
    [MyObject func1];
}
@end

もちろん、この書き方は何も悪くないし、きちんと動作する。
インスタンスメソッド/クラスメソッド、内部/外部を問わずに同じ書き方ができるということもあってか、一番よく目にするやり方のような気がする。

しかし、私はふと思った。

自分のクラス内のメソッドを呼び出すのに、自分のクラス名をいちいち指定するのはなんだか気持ち悪い。

至極当然のことであり、ご存知の方も多いとは思うが、実はコレ、別の書き方ができる。

@interface MyObject : NSObject
@end

@implementation MyObject

+(void)func1 {
    NSLog(@"hello");
}

+(void)func2 {
    // クラスメソッド内でのselfは、自身のClassオブジェクトを指している
    [self func1];
}

-(void)method1 {
    // インスタンスメソッド内のselfは、もちろん自身のインスタンスを指している
    // そして、NSObjectは、classというインスタンスメソッドを持っており、自身のClassオブジェクトを返す
    [[self class] func1];
}

@end


内部から呼び出す場合に限っては、こちらの方が圧倒的に読みやすいような気がするのは私だけだろうか。


まあ、Objective-Cのメッセージ送信はオーバーヘッドがわりとあるらしいので、後者のmethod1内での呼び出し方は多少無駄があるのかもしれないが。

1つ注意しなければならないこととして、厳密には、名前を固定する前者のやり方と、selfを使う後者のやり方は少し違う。それは、もしも上記MyObjectクラスがサブクラス化されていた場合、クラスメソッド内のselfはMyObjectクラスではなく、サブクラスを指しているかもしれないということだ。クラスメソッド内のselfが、親クラスを指しているのか、サブクラスを指しているのかは、メッセージの送信先によって決まる。Static Factory Methodを作るときなんかは、その性質をうまく利用すると良いそうな。

【参考】
あなたのFactoryメソッドをダメにする方法
http://qualitycoding.org/factory-method/


クラスメソッドで多態性を実現するサンプルというか、お試し

@interface MyObject : NSObject
@end

@implementation MyObject

+(void)func1 {
    NSLog(@"hello");
}

+(void)func2 {
    [self func1];
}

@end

@interface SubObject : MyObject
@end

@implementation SubObject

+(void)func1 {
    NSLog(@"hehehe");
}

@end

int main(int argc, const char** argv)
{
    @autoreleasepool {
        [SubObject func2];    // hehehe
    }
}

なるほど。なかなか面白いね。