Objective-CなどiOS関連の技術メモ。知識はここに投げ捨てて忘れる。

既存クラスをカテゴリ拡張したスタティックライブラリでunrecognized selectorエラー

最終更新日 2013年07月21日 18:30

カテゴリをつかって既存クラスを拡張しているスタティックライブラリをリンクしている場合、その拡張されたメソッドを呼び出すところで unrecognized selector sent to class という実行時例外が発生する。この件についてはAppleの次のドキュメントが詳しい。

Technical Q&A QA1490 Building Objective-C static libraries with categories

実行時に動的リンクする場合、既存のクラス実装(たとえばNSString)のオブジェクトコードと、それを拡張したカテゴリ実装の間を結びつけることができないのが理由だという。このことはUNIXのスタティックライブラリ実装やリンカとObjective-Cのもつ動的メカニズムの間の違いによるもので、仕方のないこと、つまり仕様のようだ。

この問題は、スタティックライブラリとリンクしているターゲットのBuild Settingsで、Other Linker Flagsに -ObjC を設定することで解決できる。-ObjCフラグについては、Appleが公開しているldのmanpageを見てみよう。

-all_load
Loads all members of static archive libraries.
-ObjC
Loads all members of static archive libraries that implement an Objective-C class or category.
-all_load
スタティックライブラリの全メンバをロードする
-ObjC
スタティックライブラリのメンバのうち、Objective-Cのクラスまたはカテゴリを実装しているものを全てロードする

ld(1) Mac OS X Developer Tools Manual Page

-ObjCフラグをつけることで、スタティックライブラリ(のうちObjective-Cクラスとカテゴリ)については、実行ファイル作成時にロードしてリンクすることができる。これによって各種フレームワークに含まれるObjective-Cクラスなどもロードすることになり、実行ファイルはかなり肥大化するわけだが、ともあれ、問題は解決する。

ただし、ひとつ例外がある。以下、先ほどのAppleのドキュメントTechnical Q&A QA1490の続き。

Important: For 64-bit and iPhone OS applications, there is a linker bug that prevents -ObjC from loading objects files from static libraries that contain only categories and no classes. The workaround is to use the -all_load or -force_load flags.

重要:64ビットiOSアプリケーションではリンカにバグがある。そのせいで、-ObjCフラグは、カテゴリのみでクラスがひとつも含まれないスタティックライブラリからオブジェクトファイルをロードできない。この問題は、-all_loadまたは-force_loadフラグをつかうことで回避できる。

以上、まとめると、

  • Objective-Cの仕様として、カテゴリで既存クラスを拡張しているスタティックライブラリは動的リンクに失敗するので、リンカに-ObjCフラグを指定してビルド時に静的リンクしてしまうこと
  • ただし、そのスタティックライブラリがクラスをひとつも含まずカテゴリのみで構成されている場合は、リンカのバグにより-ObjCが機能しないので、-all_loadまたは-force_loadを使うこと

詳解 Objective-C 2.0 第3版

必読。こんなゴミサイトより、こっちを読むべき。

Amazonでみる

iPhoneプログラミングUIKit詳解リファレンス

必読。こんなゴミサイトより、こっちを読むべき。

Amazonでみる