ARC有効時はC言語の構造体にObjective-Cオブジェクトは使えない

注意: この記事は1年以上前に掲載されたものです。情報が古い場合がありますのでお気を付け下さい。

もう、新しいプロジェクトではAuto Reference Counting(ARC)を当たり前のように使っているようになりつつあるが、必ずしもそうとは言えない場合もある。まだARCに有効にしていない段階で、ARCを有効にする際に気をつけなければならない点がある。それが「C言語の構造体にはObjective-Cオブジェクトは使えない」ということである。

ARCを有効にすると使えなくなる例

以下のコードではARC無効時では使えるが、ARCを有効にすると使えなくなる例である。

typedef struct _foo {
    int identifier;
    NSString *name;
}foo;

上記のコードはARC無効時は問題なく使えるが、ARCを有効にすると「ARC forbids Objective-C objects in structs or unions」というエラーメッセージが表示されてコンパイルが通らない最たる例である。これは、C言語の構造体の特性上、C言語の構造体にObjective-Cのオブジェクトを定義してしまうといつretain/releaseすればいいのかわからなくなってしまうという問題から来ている。

その対策として、以下のいずれかの方法で問題を解消させる必要がある。

対策1: Objective-Cのクラスに置き換える

考えられる方法の一つとして、構造体をObjective-Cのクラスに置き換えてしまう方法である。この方法の場合は、以下のように書けば問題ないと考えられる。こうした場合は、各構造体を使用した箇所をObjective-Cのクラスに合わせて書き換えなければならないが、コンパイルが通らないという問題については根本的に解決される。

ヘッダーファイル

@interface foo : NSObject
@property int identifier;
@property NSString *name;
@end

実装ファイル

[code lang=”objc”]@implementation foo
@end[/code]

対策2: Objective-C++としてビルドを行う

もう一つ考えられる方法として、C++としてビルドを行う方法である。この場合は、以下のように若干手直しをした後、Objective-C++のコードとしてビルドするという方法である。Objective-Cとしてコンパイルするための最も簡単な方法としては拡張子を「.m」から「.mm」である。

struct foo {
int identifier;
NSString *name;
};

C++における構造体では多くの実行環境では実装上クラスと同様の存在として扱われる ((デフォルトのアクセス権が構造体ではpublic、クラスではprivateとなっている程度しか違いがない)) ため、C++においては構造体は破棄されるときにクラスと同様にデストラクターが呼ばれるようになっている。その際に自動的にデストラクターにreleaseメソッドが呼ばれるようになる。したがって、C言語ではあった問題が解消される形になる。

この方法ではObjective-Cのクラスに切り替える方法と比較すると修正が少なくなるかもしれないが、コンパイル速度が遅くなる、別のバグを引き起こす可能性が比較的高いため、個人的にはあまり勧められない。

最後に

今回はARCを無効にしている場合を想定して、ARCを有効にした際の構造体を使用する上での注意点とその対策法をあげてみた。今後はSwiftの登場もあり、より一層ARCやModern Objective-C Syntaxなどを使いこなせるようにすることが求められるだろう。

タイトルとURLをコピーしました