[Objective-C]Category のOver Rideは避けた方がいい

Objective-Cにはカテゴリと呼ばれる既存のクラスにメソッドを追加する機能があります。その追加するメソッドは複数ファイル分けて記述することができます。もし、2つのファイルに同じメソッドが定義された場合はどうなるかという話です。

例えばNSArrayにfというメソッドを2つのファイルで追加することにします。

// NSArray+Test1.h
#import <Foundation/Foundation.h>
@interface NSArray (Test1)
- (void)f;
@end
// NSArray+Test1.m
#import "NSArray+Test1.h"
@implementation NSArray (Test1)
- (void)f {
    NSLog(@"Test1");
}
@end
// NSArray+Test2.h
#import <Foundation/Foundation.h>
@interface NSArray (Test1)
- (void)f;
@end
// NSArray+Test2.m
#import "NSArray+Test1.h"
@implementation NSArray (Test1)
- (void)f {
    NSLog(@"Test1");
}
@end

1から順にファイルを作ったとします。
この4つのファイルがある状態で以下のコードを実行したら、

// main.m
#import <Foundation/Foundation.h>
#import "NSArray+Test1.h"

int main(int argc, const char * argv[])
{
    [[NSArray array] f];
    
    return 0;
}
Test2

と表示されます。
あくまで予想ですが、Objective-Cのカテゴリはビルド時にプロジェクト全体でメソッドを追加しているため、先にビルドされたTest1側のメソッドはTest2に上書きされたのではないかと思われます。つまり、ビルドする順番を変えれば、Test1のメソッドが使える。さらに言えば、同一のメソッド名の中で最後にビルドされた一つしか使えないことになります。
ちなみにビルドの順番はProject -> Build Phases -> Complite Sources のファイルの上から順番にビルドされるようです。



ちなみに悪用するとこんなことができます。

// NSString+Test.h にメソッドがあるフリをする
#import <Foundation/Foundation.h>
@interface NSString (Test)
- (void)f;
@end
// main.m
#import <Foundation/Foundation.h>
#import "NSString+Test.h"

int main(int argc, const char * argv[])
{
    id obj = [NSArray array];
    [obj f];
    
    return 0;
}

importしていないけれど、NSArrayのメソッドが呼び出せるようにエラーが出ないようにNSStringに実装しているように見せかければコンパイルは通る。そして、実行しても動いているがよくわからない挙動をする。
動的型付け言語の残念なところです。


今回書いてあるコードは以下においておきました。
https://github.com/akuraru/CategoryExample