Objective-Cではカテゴリを使って既存のクラスに新しいメソッドを追加したかのように振る舞わせることができる。

では、カテゴリで新しいメソッドではなく、既存の(というかオーバーライドして使うようなもの)を定義するとどういう挙動になるのか。気になって調べてみた。

例えばUIViewでタッチを検出して処理を行いたい場合はそのビューのサブクラスを作ってtouchesBegantouchesEndedを実装すればよいし、ViewControllerでも同じようなことができる。

このtouchesBeganなどのメソッドをカテゴリで実装してやる。実装は単にタッチ開始や終了を自身のアドレス付きでログに出すものとなっている。

// UIView+Touch.h
#import <UIKit/UIKit.h>

@interface UIView (Touch)

@end
// UIView+Touch.m
#import "UIView+Touch.h"

@implementation UIView (Touch)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"touchesBegan:[%p]", self);
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"touchesEnded:[%p]", self);
}

@end

そして、こんな感じで適当にビューを作る。置いたのはすべてUIView。UILabelなんかでもいいんだけど、一部のUIコンポーネントはuserInteractionEnabledをYESにしないといけないのが面倒なのでサンプルではUIVIewにした。

起動して次の順番でそれぞれのビューをタップする。

  1. UIView 1
  2. UIView 2
  3. UIView 3
  4. 背景部分のView

ログにはそれらのViewのtouchesBegantouchesEndedからの出力が出ている。

2014-05-30 21:17:36.592 OverrideInCategory[56354:60b] touchesBegan:[0x8f3b190]
2014-05-30 21:17:36.641 OverrideInCategory[56354:60b] touchesEnded:[0x8f3b190]
2014-05-30 21:17:37.585 OverrideInCategory[56354:60b] touchesBegan:[0x8f3aac0]
2014-05-30 21:17:37.675 OverrideInCategory[56354:60b] touchesEnded:[0x8f3aac0]
2014-05-30 21:17:38.639 OverrideInCategory[56354:60b] touchesBegan:[0x8f3b7e0]
2014-05-30 21:17:38.757 OverrideInCategory[56354:60b] touchesEnded:[0x8f3b7e0]
2014-05-30 21:17:39.953 OverrideInCategory[56354:60b] touchesBegan:[0x8f3b130]
2014-05-30 21:17:40.061 OverrideInCategory[56354:60b] touchesEnded:[0x8f3b130]

先ほど定義したUIView+Touchのヘッダーはどこにも#importはしていないが、全てのViewに対してtouchesBeganなどが有効になっている。こういう言語仕様になっているということを知らなかったので、へぇーという感じ。