|
最新の記事
カテゴリ
おすすめキーワード(PR)
ファン
|
こないだiPhone Hacksっていう本がオライリーから出ていたので、手にとって読んでみたらなんと、VFP( Vector Floating Point) がiPhoneにあるってことを初めて知ったorz
VFPとは、ベクトルのx, y, z, wの浮動小数型4要素を並列に計算してくれるもの。 ただ、CPUがCortexじゃないのでNEONイントリンジック(C++から関数の形で呼び出せるマクロのようなもの)が使えないはずで、インラインアセンブリかアセンブリで記述するしかないのと思われる。 特に行列やベクトルの演算, スケルタルアニメーションなどでは、VFPを使うと使わないとではパフォーマンスに大差がでるだろう。 VFPとか扱うの結構好き。パイプラインを如何にストールさせずに流すかとか、ns単位でベンチとりながら地味に最適化する作業が楽しすぎる。1サイクルたりとも無駄にしない勢いでちょっと偏執狂気味かも。 MIPSのカスタムCPUを使っている某携帯ゲーム機の仕事でもそんなことを延々とやっていた。 今は別の携帯ゲーム機で同じようなことを延々とやっているわけだが。
予約していたiPhone4が来ました。
この液晶の奇麗さは半端じゃないですね。 人間の眼の解像度を超えているドットピッチで、Safariの細かい文字もちゃんと読めます。 AmazonのKindleもフォントを最小にしてもとても奇麗で読みやすいです。 Kindleが読みやすくなる事を期待して買ったので、これだけでも十分価値があります。 処理速度は、初代iPhoneと比べると格段に速くなっています。初代iPhoneにiOS4を入れた後、かなり重くなってしまいましたが、iPhone4では快適に動作します。 残念なのは、外枠のステンレス部分がアンテナになっていますが、このWi-Fi用のアンテナと通話用のアンテナを手でショートさせると、本当に通話できなくなります。 あと、カメラを通した映像の中央部分が微妙に青くなる不具合も確認しました。 まぁ、5万円でこのスペックですから、と、言いたくなりますが、5万円あったら、あのスペックのPS3とXBOX360 valueが買えてしまう! apple.comで、アンテナ問題を釈明するビデオがアップされてました。今は見れないようですが。 それ見てかなりアタマに来ましたね。冒頭に変な音楽が流れるのですが、その中で、『iPhone4を気に入らないなら、買うなよ。iPhone4を買ってしまって気に入らないなら、返品しなよ。』とか超開き直った歌詞。色んな問題を抱えたユーザーに対して、こんなビデオを見せる人間の神経が知れません。自分は、アンテナの問題は、テープをごく一部に張る事でなんとか対処し、カメラについてはどうでも良い程度なんですが、気にする人にとっては酷い話。 それと、莫大な費用をかけた電波試験施設を自慢してましたが、逆に、あんな仰々しい施設があるのにこんなしょうもない問題すら発見できなかったの?という失笑を買っただけでした。 かなり間抜けで無神経なビデオに社内の誰かが気づいたのか、早々に消えていました。 ジョブスも次々にやってくる訴訟案件にかなり焦ってシナリオを書いたのでしょうかね。 対応一つ誤ると、どんでもないしっぺ返しがきますよ。
iPhoneのCPUはARM7系ですが、ARM7にはThumbモードという16bitインストラクションモードがあり、このモードでコンパイルすることによって機械語のオペコードとオペランドがコンパクトに圧縮されコードサイズを20%以上小さくすることができます。
ただ速度的なペナルティがあって、非Thumbモードと比べると20%くらい落ちます。 iPhoneのGCCコンパイル設定ではデフォルトでThumbモードがオンになっており、これをオフにすることでパフォーマンスの向上が期待できます。
※記述に誤りがあったので修正しました。2/13
UITextFieldをユーザーがタッチするとソフトウェアキーボードが現れ文字の入力が行われるわけだが、入力文字数を制限したい時がある。 その場合、 UITextFieldDelegate プロトコルの- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)stringをオーバーライドする。このメソッドはデリゲート(コールバック)であり、引数の条件での入力を許可したい場合は YESを、許可しない場合はNOを返すようにしてやる。 それぞれの引数の意味はマニュアルでは, textField そのテクストを含むtextField range リプレイスされる文字列の範囲 string リプレイスする文字列 とあるが、ソフトウェアキーボードからの入力があった場合、実際には、range=変更元の文字数, string=追加する文字という意味に近い。 具体的には、text フィールド変数に、'abc'が入っているとして、ユーザーが'd'をキーボードから入力した場合、 textField.text = @"abc" range.location = 3 range.length = 0 string = @"d" となる。 文字数制限をするには、このrange引数で現在入力済みの文字数を調べ、文字数が規定の文字数以内であればYESを、規定外であればNOを返してやればよい。 以下の例の場合、UITextFieldのインスタンスであるnameEditorがキーボードから文字を受け取るたびに textField:shouldChangeCharactersInRange:replacementString: デリゲートを呼び、文字を追加して良いものかどうか尋ねる。nameEditorはこのデリゲートがNOを返してきた場合、入力文字を追加しない。 @interface MyController : UIViewController<UITextFieldDelegate> { UITextField* nameEditor; ... } .... @end @implementation MyController ... - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { BOOL change = NO; if( textField == nameEditor ) { if( range.location + range.length + [ string length ] < MAX_NAME_LENGTH ) { change = YES; } } return change; } ... @end 候補選択モード(入力中に入力文字列が水色のハイライトで表示される)で入力された場合、上記の動作とは異なる。 例えば、textField.text に@"abc", 候補が@"de"で更に@"f"を入力した場合、 range.location == 3 range.length == 2 string == @"d" [ string length ] == 1 となる。1文字入力モードの時は、range.length == 0 となり、候補選択モードの場合は、候補文字列の長さが入る。 ちょっと不可解で上記の情報が確実とは言えないので注意していただきたい。
Java +connector/j + MySQLで iPhoneアプリ用のサーバーを書いていたのだが、NSStreamの動作が 若干おかしく、仕様通りに動作しない。
"connection reset by peer"などのエラーが起きると、仕様では、eventCodeとしてNSStreamEventErrorOccurredを送ってくるのだが、実際にはNSStreamEventHasBytesAvailableが送られ、そのまま read:maxLengthメソッドを呼ぶと、-1(0xffffffff)を返してくる。 なので、リード長(read:maxLengthの戻り値が -1の場合は、下のコードの太字部分のように一度ハンドラーを抜けてやる。そうすると次のターンでNSStreamEventErrorOccurredが送られてくる。 - (void)stream:(NSStream *)stream handleEvent: (NSStreamEvent)eventCode { ... case NSStreamEventHasBytesAvailable: { if( !data ) { data = [ [ NSMutableData data ] retain ]; } uint8_t buf[1024]; unsigned int len = [ (NSInputStream *)stream read: buf maxLength: 1024 ]; if( len == 0xffffffff ) { return; } else if( len > 0 ) { [ data appendBytes: (const void *)buf length: len ]; } } break; case NSStreamEventEndEncountered: { [ stream close ]; dataIsReady = YES; if( callbackFunc ) { callbackFunc( Stream::NO_ERROR, userData ); } } break;
私の中ではネットワークプログラミングといえばJAVAだが、JAVAのそれは素晴らしいの一言につきる。すっかりその楽ちんさに馴れてしまった私としては、まいった。iPhoneではそうは行かない。CFNetworkとかったる過ぎるし、いまさらレガシー言語のライブラリなんか使いたくない。
そこで、NSStreamなわけで、"Stream Programming Guide for Cocoa"の"Setting Up Socket Streams"章を読んで、なんかRunLoopにフックしてAsynchronousにしている部分とか気になるが、その辺は見なかった事にしておおむねオーケーということで、とりあえず、マニュアルのサンプルコードをほぼ丸写ししてコンパイルしてみた。 まいった。シミュレータ用のビルドだと問題ないのだが、実機用のビルドだと、NSHostの hostWithAddress などというメソッドも、NSStream getStreamsToHost:port::inputStreamなどというメソッドも存在しないとコンパイラに怒られる。Obj-Cの場合、オブジェクトに存在しないメッセージを送ると普通にぶっ飛ぶので、放ってはおけない。 訳が分からないので、とりあえずNSHost.hをインクルードしてみると、NSHost.hがないという。はぁ?と怪訝に思いながら、terminalを開き findしてみる。 すると、SimulatorのライブラリパスにはNSHost.hが存在するが,iPhoneOSのそれにはない。NSStream.hの方はというと、getStreamsToHost:port の宣言が、実機だったらはじくようにしてある。 ネットで調べると、実は実機にはそれらのセレクターはちゃんと実装されているらしい。だから、警告を無視しても問題なく動作しているそうだ。つまるとこ、これらはプライベートになっていて使用禁止なんだろうと。 なんなんだろうか? 本当になんなんだろうか? マニュアルのサンプルコードで紹介している重要な、なくてはならないメソッドが使用禁止なんて事があり得るのだろうか? とはいえ、代替手段がCFNetworkへの退行しかないとなると、ここはNSStreamを使っちゃうしかない。 そこで、書き込みを信じ、実機でテストしてみる事に。 まず、コネクト要求が来たら、文字列を送り返すだけの簡単なリッスンサーバーをLinux上のJavaで書く。使用するポートをFirewallで開ける。ifconfigでip addressを調べる。これでサーバーの準備完了。 iPhone側は、"Stream Programming GUide for Cocoa"マニュアルのサンプルコードをほぼ丸写しでクライアントコードを書く。 ip addressを生で入力する為に、NSHost hostWIthNameの代わりに、hostWithAddress:(NSString*)を使用。 コンパイル、警告を無視、実機転送、起動、自宅無線LANとのWiFi接続確認(3G接続だとグローバルIPアドレスが必要になり、port->local adress変換をルーターで設定しなければならない) サーバーからのメッセージ受信成功。 やっぱり、大丈夫みたいです。 (こういう苦労はしたくないんです。不毛です。頼みますよ。。。本当に。。。)
コンパイル後のバンドルは以下のディレクトリの16進数キーコード(e.g. CF778331-B9AB-4F4A-8209-C0E...)の名の付いたフォルダにコピーされる。
/Users/[home]/Library/Application\ Support/iPhone\ Simulator/User/Applications 沢山キーコードのディレクトリがあって探すのが大変なので、 > cd /Users/[home]/Library/Application\ Support/iPhone\ Simulator/User/Applications >find . -name [app name] とやって検索。ファイル名はケースセンシティブ(大文字小文字が区別される)なので注意。 また、バンドルのディレクトリ名は、ビルドしてコピーされる度に変わるので注意。 ところで、開発用のSimulatorでこんなコードのディレクトリ名にする必要があるのだろうか? セーブしたファイルを確認するのも一手間だ。 参照: iPhoneAppProgrammingGuide, File And Data Management: "Commonly Used Directories ", "Getting Paths to Application Directories"
オーディオファイルのコンバートに関して、素晴らしい記事を発見。
http://iphone-dev.ensites.net/archives/150 本当の本当にありがとうございます。 コマンドラインユーティリティに afconvert という、オーディオファイルコンバーターがあるのでそれをterminalで利用する。もしパスが通ってなければ、/usr/bin にあるはず。 .WAVファイルのサンプリング周波数、帯域ビット数の変更は、-d オプションでコンバートできる。 { -d | --data } data_format[@sample_rate_hz][/format_flags][#frames_per_packet] : [-][BE|LE]{F|[U]I}{8|16|24|32|64} (PCM) e.g. BEI16 F32@44100 or a data format appropriate to file format .WAVファイルの指定可能なデータフォーマットは以下の5つ UI8 LEI16 LEI24 LEI32 LEF32 例えば、-d LEI16@22050で、16ビット 22050Hz を表す。 -c オプションでチャンネル数を指定する。 で、wavファイルのサンプリング周波数、帯域ビット数を変換するには, -f オプションでファイルフォーマットを指定し、-d オプションでデータフォーマットを指定する。 >afconvert -f WAVE -d LEI16@22050 -c 1 カレントとディレクトリ内の全ての .wavファイルを変換するには、findコマンドを使用する。 >find . -name '*.wav' -exec afconvert -f WAVE -d LEI16@22050 -c 1 {} \; {} で置換される引数を表し、; で終わりを表す。が、セミコロンをエスケープしなければならないのでバックスラッシュ(\)を付ける。 .WAVファイルを.CAFにコンバートするには、-f オプションでファイルフォーマットを 'caff'に指定し、-dオプションでデータフォーマットを'ima4' に指定する。 { -f | --file } file_format: —ーーーー 略 ————— 'caff' = Apple CAF (.caf) data_formats: '.mp1' '.mp2' '.mp3' 'MAC3' 'MAC6' 'QDM2' 'QDMC' 'Qclp' 'Qclq' 'aac ' 'aacl' 'agsm' 'alac' 'alaw' 'drms' 'dvca' 'dvi ' 'dvi8' 'ilbc' 'ima4' 'lpc ' I8 BEI16 BEI24 BEI32 BEF32 BEF64 LEI16 LEI24 LEI32 LEF32 LEF64 'ms\x00\x02' 'ms\x00\x11' 'ms\x001' 'ms\x00U' 'samr' 'ulaw' 'vdva' 以下で、.cafファイルにコンバートする。 >afconvert -f caff -d ima4 < source file > カレントディレクトリ内の .wavファイルを全て .cafにコンバートするには,先ほどと同様, >find . -name '*.wav' -exec /usr/bin/afconvert -f caff -d ima4 {} \; とする。 ちなみに、元の.wavファイルが 44kHz ステレオ 16bit PCM(べた)で、260kバイト。 これを 22kHz モノラル 16bit IMA4(AD-PCM)にコンバートして、24kバイトと、ファイルサイズが1/10以下になった。 つーか、モバイルデバイスにPCM 44kHzってやばすぎた。
2chでリジェクトされた人のリジェクト理由。
"ネットに接続する機能のある App なんだが iPhone が Airplain Mode になってる最中に動作させたら接続に失敗する この失敗した旨をユーザに通知しないといけないらしい" info.plistの、UIRequiresPersistentWiFi=true にしたら通ったらしい。 info.plistをXcode上で編集すると、値をチェックボタンにする仕方が分からないので、テクストエディットで開いて編集する。 以下の2行を加えればok. <key>UIRequiresPersistentWiFi</key> <true/> WiFi接続を永続的にするってことか? 121,634,816
セレクタの関数ポインタ化と、呼び出しはちょっと面倒くさい。
NSObjectクラスの methodForSelector: メソッドを使用する。 - (IMP)methodForSelector:(SEL)aSelector このメソッドはaSelectorで示されるメソッドの実際のアドレスを返す。 //なんらかのタイミングでコールバックするクラス @interface Invoker { id instance; SEL selector; int someArg1; void* someArg2; } @end @implementation Invoker //コールバック登録 - (void)register: (id)_inst selector: (SEL)_sel { instance = _inst; selector = _sel; } //コールバックを実行 - (void) invoke { IMP method = [ inst methodForSelector: sel ]; method( instance, selector, someArg1, someArg2 ); } @end IMPは以下に定義される通り、可変引数リストを持つ関数ポインタである。 typedef id (*IMP)(id, SEL, ...); - (void)someFunc { //コールバックを登録 Invoker* obj = [ [ Invoker alloc ] init...]; [ obj register: self selector: @selector( someselector: ) ]; ... } //コールバックセレクタ定義 // 引数は任意の個数、型を定義できる。もちろん引数をなしにもできる。 - (void)someselector: (int)someArg1 somePointer: (void*)ptr { … } < 前のページ次のページ >
| |||||||||||||||||||||||||||||||||||||||||||||||||||