10月 | 2012 | HMDT Blog

カテゴリー : 2012年 10月

UICollectionViewCellのサブクラスをiOS 5で共存させる


iOS 6ではUICollectionViewってのが追加された。テーブルビューを超えるとっても便利なクラス。でも、これを使ったアプリをiOS 5でも動作させようとしたら、ちょっとした問題が。

新機能を使ったアプリを過去のバージョンのOSで動作させる場合、いくつかのテクニックがある。新規フレームワークはweakリンクするとか。新規クラスは直接記述せずNSClassFromStringで取得するとか。そうやって一個ずつクリアしていったんだけど、これどうしよう?っていう問題にぶつかった。

UICollectionViewでは、UICollectionViewCellっていうセルクラスを使う。画面に表示する内容をカスタマイズするもので、テーブルビューのセルみたいなやつだ。UICollectionViewCellのサブクラスを作るってのが、普通の使い方になる。

これが問題に。サブクラスを作るということは、ビルド時に親クラスを指定してやらないといけない。指定された親クラスは、バイナリファイルに解決が必要なクラスとして記述されて、起動時にdyldが解決しようとする。このとき、UICollectionViewCellクラスが存在しないと、起動に失敗する。従って、バイナリにUICollectionViewのサブクラスが含まれていると、そのクラスを実行時にインスタンス化するしないに関わらず、iOS 5での起動ができない。

うーむ、困った。どうしよう?やっぱりUICollectionViewを使うのをやめるとか、iOS 5は対応しないとか、後ろ向きな解決策も考えた。または、iOS 5でも動作する互換クラスを使うとか(PSTCollectionViewみたいな)。けど、釈然としない。おれは、iOS 6ではUICollectionViewを使って、iOS 5では使わないっていう、そういうバイナリを作りたいんだよ。

いろいろ悩みまくったあげく、トリッキーな解決策にたどり着いた。要は、UICollectionViewCellのサブクラスが起動時に存在するから悪いのだ。ならば、始めはUIViewのサブクラスにでもしておけばいい。そして、起動した後で、その親クラスを差し替えればいいのだ。

「親クラスを差し替える?」なんだそりゃ、と思われたそこのあなた。Objective-Cではそんなことだってできてしまいます。だって、変態言語だもの。

Objective-CのランタイムAPIには、class_setSuperclassというのがある。文字通り、親クラスを設定するためのAPIだ。これを使えば、あるクラスの親クラスを動的に変更することができる。もちろん、下手にこんなことをすればとんでもないことになるので、使用には細心の注意が必要だ。ドキュメントには一言、「You should not use this function.」とだけ書いてある。そんなら、こんなAPI用意しておくなよ。

実験してみたloadメソッドに次のように書く。

+ (void)load
{
    // UICollectionViewCellのクラスを取得
    Class   collectionViewCellClass;
    collectionViewCellClass = NSClassFromString(@"UICollectionViewCell");

    // 存在する場合
    if (collectionViewCellClass) {
        // 親クラスを差し替える
        Class   magazineCollectionCellClass;
        magazineCollectionCellClass = NSClassFromString(@"HJMagazineCollectionCell");
        class_setSuperclass(magazineCollectionCellClass, collectionViewCellClass);
    }
}

これを使えば意図通りの結果を得ることができた。とっても気持ち悪いが、とりあえずこれでしのげるかどうか、しばらく試してみる。しかし、Objective-Cフリーダムすぎる。

iCloudの呪い


iCloudを使おうとして開発を進めて、諸般の事情によりiCloud機能をドロップしたプロジェクトがいくつかあるんだよね。iCloudに関するソースコードは、コメントアウトして通らないようにしてある。

そんなプロジェクトのアプリをテスト中、急に動かなくなる事態が発生している。これ、固まる訳じゃなくて、動かなくなるんだけど10秒くらいすると復活する。しばらくは問題なく動いているんだけど、また急に止まってしまう。

どこで止まっているんだ、とデバッガを使って調べたところ、iCloud関係のメソッドの中でブロックされているようだ。想像するに、データにアクセスするところでiCloudサーバを調べにいっちゃってるんじゃないかと。でも、もうiCloud機能ドロップしているんで、アプリのiCloud設定も全部落としてある。それで止まっちゃっている。

呪いだ!iCloudの呪いだ!

この呪いの嫌らしいところは、ビルドのクリーンをしたり、デバイスからアプリを削除しても治らないこと。iCloudサーバにデータが残っていると、サーバに接続して同期しようとするみたい。なにせクラウドだからな!

じゃサーバのデータを削除しようか、と思ってもその手段がない。developer.cloud.comとかを使ってサーバのデータを確認すると、確かに怪しいデータが残っている。でもこれ、見るだけで削除できないんだよね。なんてこったい。

「mkinoは呪いでアプリが動かない!」

これは教会でお祓いしてもらわないとダメかもしんない。

ちなみにこの問題は、開発中に使ったアカウントでしか発生しないので、リリースする分には問題ない。あと、iOS 5でしか発生しない。iOS 6は大丈夫っぽい。iOS 6は何気にiCloudが改善されている。