#import の使いどころ:循環参照しないために。

!注意!
この内容はあまり正しくありません。Objective-Cの循環参照についてとして、書き直しました。こちらを参照ください。


Objective-C を書いていてたまに出会うのが、循環参照の問題。

error: expected specifier-qualifier-list before 'MyClass'

循環参照していると、こののように定義が無い、という旨のエラーが出る。
ClassA と ClassB という二つのクラスがあるとする。それぞれ相互にヘッダの中で import すると、この循環参照が発生する。

[ClassA.h]
#import "ClassB.h"

@interface ClassA {
  ClassB variableB;
}
@end
[ClassB.h]
#import "ClassA.h"

@interface ClassB {
  ClassA variableA;
}
@end

これを解決するには、ヘッダではなく、実装ファイル(ClassA.m、ClassB.m)のほうで import する。しかし、それでは ヘッダ中のクラス名が解決できないため、ヘッダには @class ディレクティブでクラス名であることを宣言する。

[ClassA.h]
@class ClassB;

@interface ClassA {
  ClassB variableB;
}
@end


[ClassA.m]
#import "ClassB.h"
...
[ClassB.h]
@import ClassA;

@interface ClassB {
  ClassA variableA;
}
@end

[ClassB.m]
#import "ClassA.h"
...

さて、ここからが本題。

そもそも、ヘッダファイル中にヘッダの import を書くから循環参照が起きてしまう。ならば、最初からヘッダファイルではなく、実装ファイルの方で import すればよいのではないか。それで、必要に応じてヘッダファイル中で、@class や @protocol ディレクティブで宣言すれば良さそう。
ただし、基盤フレームワーク、UIKit/UIKit.h みたいなのは ヘッダファイルで import してもよいかもね。循環参照の恐れは無いし、いちいち @class で宣言するのはめんどくさい。

ということで、まとめ。

  • 自分が作ったクラス、プロトコル等を他のクラスで使用する場合は、実装ファイルで import する。
  • ヘッダファイルで出てくる場合 @class, @protocol で宣言する。
  • フレームワーク等の、循環参照しないものは ヘッダファイルで import する。