yashiganiの英傑になるまで死ねない日記

週末はマスターバイクでハイラルを走り回ります

顕在化するmainBundleリスク

iOS向けのアプリケーションやライブラリで画像やローカライズファイルなどのリソースを使うとき,bundleという仕組みを利用します. bundleはアプリケーションやライブラリに組み込まれ,実行時に各リソースファイルとの橋渡しをします. 例えば,ローカライズに使うNSLocalizedStringマクロはこのように定義されています.

#define NSLocalizedString(key, comment) \
       [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]

[NSBundle mainBundle]は実行中のアプリケーションbundleを返します. tableが実際に使用されるローカライズファイルですが,nilだとデフォルトのLocalizable.stringsが利用されます. NSLocalizedStringsはここから定義された文字列を探し,返すというわけです. ここからはリソースにアクセスするためにbundleの仕組みが利用されていることがよくわかります.

顕在化する[NSBundle mainBundle]リスク

長らくiOSアプリではライブラリの組み込みにリンクでは無く,アプリケーションへ直接組み込むことが標準的な手法でした. しかし,WWDC2014においてiOSにもdynamic frameworkが解放されました. それに伴いCocoaPodsやCarthageなどの構成管理ツールもdynamic frameworkへの対応が進められています.

さて,先ほどのbundleですが,dynamic frameworkに対応するとどうなるのでしょうか? frameworkを使うと,bundleはアプリケーションではなくそのframeworkそのものにbundleされます. つまり,frameworkを使った際はbundleからデータをロードする際,frameworkのbundleからリソースをロードするためにこのメソッドを使うことができません.

そこで,ライブラリ側でのbundleのロードについては,[NSBundle bundleForClass:]を利用することでうまくいきます.

let bundle = NSBundle(forClass: TheLibrary.self)

ライブラリで使っているクラスからbundleを特定することでframeworkのbundleにアクセスできます. 従来の方式でもbundleもろともアプリケーションに直接埋め込んでいたのですから,当然動作します([NSBundle mainBundle]と同じものが返ってくる).

まとめ

ローカライズや独自のリソースを使っているライブラリの多くは[NSBundle mainBundle]を使っているでしょう. これは完全にcontributeチャンスですので,心当たりがあればすかさずcontributeいたしましょう. 筆者も既に2つのプロダクトにcontributeしております.

参考