頭と尻尾はくれてやる!

iOSアプリなどの開発日記です


サマータイムがある所で不具合発生してた

なんだか2月になってからというもの海外のユーザーさんから、おめえのアプリがおかしい!2月が表示されねえぞ!ってメールが続いたんだよ。
アプリは「赤ちゃんの成長グラフ(Lite)」なんだ。赤ちゃんの身長や体重を入力する月ごとの表があるんだけど、2月が表示されず、1月の次が3月になるとか、、、
あるユーザーさんに使用環境とか聞いて試してみたら、あっさりと再現したんだよ、、、orz
そういえば前々からアプリのレビューで同じことを書いてる人もいたなあ、と今になって気付いたよ。このことだったんだ、って。

何が悪いんだって調べていくと、どうもフレームワークが悪いんじゃね?って思ってさ。しばらくほっといたら次のiOSのバージョンアップで直るパターンかな、なんて思ったんだよ。
でも、さらに調べるとバグじゃなくて仕様っぽい、、、いや、単に俺の勉強不足だね。どうやらメソッドが予想外の結果を返すのはサマータイムの影響みたい、ってことに落ち着いてさ。
なんとか昨日、修正版を申請したよ。でもまだリリースまでは時間がかかるだろうから、しばらくはどうなってんねん?!ってメールが届くのかなあ。


あまり一般的じゃないからコードは書いても仕方ないかもしれないけど、一応。
コード内で日付をNSDateオブジェクトとして持ってるんだよ。
ただ、意味があるのは年月日の数字だけで時間は関係ないので、"20120208"なんて文字列でもよかったりするんだけどね。
このNSDateオブジェクトは時間の情報が不要だから、次のようにして作っていたんだよ。
{
    NSDateComponents *comps1 = [[[NSDateComponents alloc] init] autorelease];
    [comps1 setYear:2012];
    [comps1 setMonth:2];
    [comps1 setDay:8];
    NSCalendar *calendar1 = [NSCalendar currentCalendar];
    [calendar1 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
    
    NSDate *date1 = [calendar1 dateFromComponents:comps1];
    
    NSLog(@"date1 : %@",date1);
]
ここでNSLogのアウトプットはこんなの → date1 : 2012-02-08 00:00:00 +0000
ともかく、こういうNSDateオブジェクトがあるとするね。

この日付の三ヶ月後が欲しいとするよ。
{
    //3ヶ月後を得る
    NSDateComponents *comps2 = [[[NSDateComponents alloc] init] autorelease];
    comps2.month = 3;
    NSCalendar *calendar2 = [NSCalendar currentCalendar];
    NSDate *date2 = [calendar2 dateByAddingComponents:comps2 toDate:date1 options:0];
    
    NSLog(@"date2 : %@",date2);
}
これでdateに対して三ヶ月後の日付を持つNSDateオブジェクトを得ることができるんだ。いや、できると思っていたんだよ、俺は。
実際、上のコードを実行するとたいていの人はこんな結果になると思う。

結果1

↑コンソール部分のスクショだけど、期待通り三ヶ月後の日付が表示されてる。
ところがこのコードだとサマータイムがある地域では具合が悪いんだよね。
iPhoneの設定アプリで
一般 / 日付と時刻 / 自動設定
をオフにして時間帯を他のところにしてみる。

時間帯を変更

↑シカゴにしてみた。不具合報告メールをくれた人がシカゴだったからで、行ったことも思い入れもないよ。 これで先ほどのコードを実行してみると、、、

結果2

↑サマータイム切り替え時をはさんでいるのがミソなんだけど、サマータイムが考慮されて1時間ずれてる。日付部分の数字を拾うとおかしなことになる、、、ってのが今回の不具合の要因だったんだよ。

サマータイムって切り替わるのが3月とか秋とかでしょ?
そういえば、USAのレビューに
OK...entered October and was hoping to see November, yet there wasn't one. Saw December twice....very useless.
なんて書いてたんだ。最初見た時はなんのこっちゃ?って感じだったけど、ようやく点と点がつながったよ。

[calendar2 setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
↑これを追加して期待通りのNSDateオブジェクトを得ることができたよ。

時間帯を変更することだけでチェックできるんだから今度から注意しようっと。


文章内で使う日付を今日にしたつもりが2012になってるのに今気が付いたよ。今は2013年だよね。ふう。


iOSでPOCに挑戦(3) 画像のデータを配列に入れる

頭と尻尾はくれてやる! iOSでPOCに挑戦(2) フーリエ変換その前に
の続きだよ。

vDSP_fft2d_zipというFFT処理するのに必要な引数の二つ目のDSPSplitComplexというモノについてだけど、これはリファレンスを見るとこうあるんだ。
struct DSPSplitComplex {
   float *realp;
   float *imagp;
};
ということでfloat型の実部と虚部の配列を持つ構造体ってことだよね。実部とか虚部なんて聞くとおしりがむずむずしてきそうだけど、ここに画像データを入れておく必要があるんだ。

じゃあ画像データってどういうのを用意すりゃいいんだろう?ってことになるよね。
iPhoneなどのデバイスにあるカメラを使うなら画像データのポインタは得られるからそこから白黒画像(にするかどうかは開発者次第)の輝度を持つ配列を作ればいいし、UIImageオブジェクトがあるならそこから輝度データを得ればいい。
ともかくなんらかの方法で得た輝度データが
float brArray[256*256];
に入っていることにしよう。どうせ0から255ならunsined charでええやんか、だって?うん、そのとおりなんだけど、まあ細かいことは気にせずに、、、ね。

輝度データを最初のDSPSplitComplexに入れるんだけど、実部と虚部二つあるけどどうすんのよ?って感じだけど虚部には入れなくてよくてゼロクリアだけしておく。ゼロを入れておかないとおかしくなることがあるよ。

ということでコードはこんな感じで。
{
    //メモリ確保
    DSPSplitComplex splitComplex;
    splitComplex.realp = calloc(nData, sizeof(float));
    splitComplex.imagp = calloc(nData, sizeof(float));
    
    //データのコピー
    memcpy(splitComplex.realp, brArray, nData*sizeof(float));
}
ここにはないけどnDataは256 x 256ね。
callocはゼロクリアしてくれるから、これで両方に必要なデータが入ったね、めでたしめでたし。callocでメモリ確保したから不要になったらfreeでメモリ解放が必要だよ。

残りの必要な引数の三つ目、、、えーっとどこまで行ったっけ?
void vDSP_fft2d_zip (
   FFTSetup __vDSP_setup,
   DSPSplitComplex *__vDSP_ioData,
   vDSP_Stride __vDSP_strideInRow,
   vDSP_Stride __vDSP_strideInCol,
   vDSP_Length __vDSP_log2nInCol,
   vDSP_Length __vDSP_log2nInRow,
   FFTDirection __vDSP_direction
);
↑これね、この三つ目と四つ目。
vDSPのリファレンスを読んでるとよくこのstrideってのが出てくるんだ。配列を順に読むなら1でいいし、一つとばしでいくなら2を指定するようになってる。
変数名からわかるようにrowとcolの両方があるんだけど、今回は配列を順番に読めばいいので1,0と指定する。これで先頭から一つずつ読んでくれる。

引数の五つ目、六つ目はサイズ指定だ。2の何乗かを示す数字、ここでは256 = 2^8なのでcol,rowとも8を入れればOK。

最後の引数はFFTするのか逆FFTするのかを指定するんだけど、ここでは普通のFFTなのでFFT_FORWARDって定数を指定する。ちなみに逆FFTの場合はFFT_INVERSEって定数があるんだ。

ふー、長くなったので今回はここまで!





RunKeeperと契約してないiPhoneでジョギング記録

契約が終了しiPod touch化したiPhone 3GSでジョギングのログをしてるってお話だよ。

頭と尻尾はくれてやる! iPhone 3GSのバッテリーが膨らんできたので交換してみた
↑瀕死状態のiPhone 3GSのバッテリー交換をしたらやってみたかったのがジョギングのログなんだよ。
GPSのないiPod touchじゃできないし、メインのiPhoneをジョギングに使うのって破損や故障が怖いなあと思っててさ。
まあほとんど使ってなかったiPhone 3GSなら万が一のことがあってもいいかってことで。

使っているアプリはRunKeeper。

RunKeeperロゴ

iPhone 3GSに100均で買ったジョギング用イヤホンをつないでジップロックに入れる。ちなみにiPhoneをジップロックに入れても外から操作はできるよ。もちろんイヤホンの線があるので完全には閉まらないんだけど、それはしょうがない。
これをポケットに入れて走るんだ。やっぱり、専用のホルダー欲しいところだよねえ、、、

RunKeeperのアプリ内でも音楽の設定(Playlistを選べる)機能があるみたいだけどあまり使ってないな。普通に音楽をかけてからRunKeeperを起動してる。
このアプリのいいところはペースを教えてくれるんだ。5分ごとや1キロごとに、1キロ何分くらいのペースかを音声で教えてくれる。教えてくれる時に音楽は少しボリュームが下がる程度でぶつりと切れたりはしない。英語だけどゆっくりだし問題ないよ。自分のペースがわかるってのは予想外に面白いんだよ。

地図で確認

↑GPSは機能してるので走った位置は記録してくれる。Wi-Fiが使える自宅に帰ってきたら地図でどこを走ったのか確認できる。距離や時間もわかる。
今までジョギングから帰ってきた後、Google Mapsで自分が走ってきた距離がどのくらいかちまちまと測定して自分でメモしてたんだけど、、、そういう作業が全く不要になったんだ。

すでに契約してないiPhoneでも位置情報の記録はできる(もちろんWi-Fiがなければ地図を表示させることはできないけど)というのは、ありがたいことだよね。


Lego Super Heros Movie Makerでコマ撮りしてみた

iPhoneアプリのLego Super Heros Movie Makerを使ってコマ撮りしてみたってお話だよ。

ウチの5歳の息子はレゴが大好きでさ。レゴを組み立てて遊ぶだけじゃなくてYouTubeでレゴ関連動画を見るのも大好きなんだよ。
レゴ関連の動画でよく見るのは本家レゴが作ったCGによるアニメやレゴを使ったコマ撮りアニメ。



↑こういうの。中でもこのMichael Hickox さんの作品はいろんなのがたくさんあってよく見てるよ。
俺も子供の隣で一緒に見てるうちに自分でもコマ撮りアニメって作れないかな、って思ったりしてさ。

Lego Super Heros Movie Makerって無料のiPhoneアプリをダウンロードしてみたんだ。



↑試しに作ってみたのがこれ。三脚を持っていないので俺がiPhoneを持ってじっとしつつ、息子に適当に動かしてって頼んで作ったんだ。指示したわけでもないのに船のアンテナを動かしてた。うん、確かに動いてる。
前のコマがうっすら表示させることができるので位置がわかりやすいんだけど、やっぱりコマ撮りアニメを作るならやっぱり三脚は必須だね。

でも、子供と一緒に楽しめるアプリだ。完成した動画を見て子供は大喜びだったよ。


悩ましいNSLog

NSLogを使うのって難しいよねってお話だよ。

デバックのときだけコンソールに出力する(for Apple LLVM compiler) 混沌のiPhone開発ブログ
↑へー、そうなんだ。

自動的にやるのがなんだか物騒な気がしてさ、リリース時にはいつも手動で
#define NSLog(...) {}
って書いてビルドしてるんだ。我ながら危なっかしいんだけど。

リンク先の方式だとややこしい設定も不要みたいなのでいいじゃないか!
と思って早速試してみたんだけど、Xcodeの補間が本家NSLogに比べると不十分で、NSLog_for_DEBUGが候補に出てくれるのはいいんだけど選んだ後に(@"の3つを自分で入力する必要があるみたい。

NSLog入力時

NSLogだと@を入力した時点で↑こんな感じで、タブキー使えば楽チンなんだよね。うーん、入力が増えるというのは悩ましいな。慣れれば問題ない程度かもしれないけど。

最近はBreakpoint使って変数の値なんかを確認する、ってのがマイブームなんだけどこれだと後で見られずおいおいってなることがあってさ。これまた悩ましいんだよ。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.