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

24/7 twenty-four seven

iOS/OS X application programing topics.

Objective-C でサブクラスのインスタンスから任意のスーパークラスのメソッドを呼ぶ

iPhone Cocoa Objective-C iPad

サブクラスのインスタンスからポリモーフィズムを無視して任意のスーパークラスのメソッドを呼びます。


↓ 下のように Shape クラスと Shape クラスを継承した Path クラス、および Path クラス を継承した Circle があります。
それぞれのクラスで draw メソッドをオーバーライドしています。

////////////////////////////////////////////////////////////////////////
#pragma mark - Shape
////////////////////////////////////////////////////////////////////////
@interface Shape : NSObject
@end

@implementation Shape

- (void)draw {
    NSLog(@"%@", @"Shape.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Path
////////////////////////////////////////////////////////////////////////
@interface Path : Shape
@end

@implementation Path

- (void)draw {
    NSLog(@"%@", @"Path.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Circle
////////////////////////////////////////////////////////////////////////
@interface Circle : Path
@end

@implementation Circle

- (void)draw {
    NSLog(@"%@", @"Circle.");
}

@end


↓ Circle クラスのインスタンスから draw メソッドを呼び出すと Circle クラスの draw メソッドが実行されて "Circle." と出力されます。
たまに多態性を無視してスーパークラスや、スーパークラスのさらにスーパークラスのメソッドを実行したいということってありますよね。
そういうときは 対象メソッドの IMP (メソッドを参照する関数へのポインタ) を使います。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    Shape *shape = [[Circle alloc] init];
    [shape draw]; // => Circle.
    
    SEL selector = @selector(draw);
    
    void(*pathFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Path instanceMethodForSelector:selector];
    pathFunction(shape, selector); // => Path.
    
    void(*shapeFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Shape instanceMethodForSelector:selector];
    shapeFunction(shape, selector); // => Shape.
}

@end

↑ IMP の定義のままだと ARC が戻り値を retain しようとするので、戻り値が void の関数ポインタにキャストしています。


↓ 上記のコードの出力は下記になります。

2012-10-23 13:15:20.632 Monomorphism[50033:c07] Circle.
2012-10-23 13:15:20.633 Monomorphism[50033:c07] Path.
2012-10-23 13:15:20.633 Monomorphism[50033:c07] Shape.


サブクラスのインスタンスからポリモーフィズムを無視して任意のスーパークラスのメソッドが呼べました。
↓ 試したコードの全体を載せておきます。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Shape
////////////////////////////////////////////////////////////////////////
@interface Shape : NSObject
@end

@implementation Shape

- (void)draw {
    NSLog(@"%@", @"Shape.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Path
////////////////////////////////////////////////////////////////////////
@interface Path : Shape
@end

@implementation Path

- (void)draw {
    NSLog(@"%@", @"Path.");
}

@end

////////////////////////////////////////////////////////////////////////
#pragma mark - Circle
////////////////////////////////////////////////////////////////////////
@interface Circle : Path
@end

@implementation Circle

- (void)draw {
    NSLog(@"%@", @"Circle.");
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    Shape *shape = [[Circle alloc] init];
    [shape draw]; // => Circle.
    
    SEL selector = @selector(draw);
    
    void(*pathFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Path instanceMethodForSelector:selector];
    pathFunction(shape, selector); // => Path.
    
    void(*shapeFunction)(id, SEL, ...) = (void(*)(id, SEL, ...))[Shape instanceMethodForSelector:selector];
    shapeFunction(shape, selector); // => Shape.
}

@end