iOS の socket(2) は 0 を返すことがある
iOS 4.2.1。
-1 チェックしかしていなくて、まさかここだとは思わずごちゃごちゃ迷走してしまった。。 あり得ないと思うことは表明(assert)しとくっていう基本をちゃんとしたい。
0 のときは無視して単純にもう一度 socket(2) を呼んであげるとちゃんと返ってくると言う謎。 その実装で fd も増えていかないのでとりあえずしのぐ。
iPhoneアプリケーション多言語化をpoファイルを使ってやる方法
iPhoneアプリケーションを他言語対応したいとしたら、Apple側がすでに用意してくれている方法を使うのが自然かと思う。 僕も最初はその方法を試した。ドキュメント的には以下がエントリポイントだろうか:
iOS Reference Library - Internationalization Programming Topics
しかしこの方法にはいろいろな問題があり、大きなアプリケーションを作ろうとするとストレスがかかってくるので現在は使用していない。(小さなプロジェクトでは使うこともある)
その問題とは以下のようなものだ:
- 言語ファイル(Localizable.strings)が独自形式なため、既存の翻訳ツールを使うことが出来ない
- 一つ目と少しかぶるが翻訳抜けや言語一覧などをチェックする機能がない
- ibtool のマイグレーション機能がくそで、バージョンアップのたびにリソースファイル内の文字列を頑張って置換する作業が必要になる
など。これらを解決するために現在は以下のような手法でアプリの多言語化を行っている:
- 言語ファイルには一般的なファイルである GNU gettext のカタログファイル形式(.po)を使う
- ソースファイルから
NSLocalizedString
系の呼び出しを抽出して .po ファイルの雛型を作成してくれるスクリプト(xgettext.pl)を作成 - .po ファイルを Localizable.string に変換するスクリプト(po2string.pl)を作成
- .nib ファイル内の多言語化が必要な文字情報はすべてインタフェースビルダーでは設定せず
.m
.mm
ファイルからセット(上のツール群をそのまま使うため)
と言う感じでやっている。その二つのスクリプトはこんな感じ。
Locale::Maketext::Extract
に依存している。
$ ./xgettext.pl -o resources/ja.po src/*.m
みたいな感じでまず言語カタログを作成。このファイルは Emacs の po-mode や Poedit などのツールで編集することができる。 個人的には Emacs を使い編集しているが、翻訳者のために良いツールを探しているのでおすすめがある方は教えていただきたい。
このスクリプトのポイントは何回実行しても.poファイル内のすでに翻訳済みのエントリは変更しないという点だ。 アプリケーションを更新したらスクリプトを実行し、.poファイル内の未翻訳のところのみ訳す、というサイクルを繰り返すことができる。.poファイルエディタは未翻訳エントリだけを簡単に編集できるというところも便利ポイントかな。
.po ファイルが翻訳されたら
$ ./po2strings.pl --po=resources/ja.po --strings=resources/Japanese.lproj/Localizable.strings src/*.m
などのようにしてiPhoneアプリから使える言語ファイルに書き出してやって完了。 ソースファイルを渡す必要があるのはスクリプトソースを見てもらえばわかるがただの手抜きである。
あ、ちなみにわざわざ NSLocalizedString(...)
とか書くのは面倒なので、
#define L(s) NSLocalizedString(s, nil)
とかを定義しておいて L(@"Hello")
とかでいけるようにしている。(もちろんxgettext.plはその書式にも対応している)
bREADER の PDF リフロー表示機能が素晴らしい件
BB2C というiPhone用の2chビューワアプリがあるのですが、このアプリは2chビューワとしての評価が高いだけではなく、その凄まじく軽快な動作からiPhoneアプリ開発者で知らない人はモグリだと言われるくらいの良アプリなのです。 最近その作者のブログ に電子書籍の話題が出ていて気になっていたのですが、どうやら次は電子書籍リーダーアプリを開発していたらしい。それが bREADER。
iPad を買って以来書籍はPDFにして読むのがメインになっていて、大体はi文庫HDで、検索が必要なときはiBooksかMac上で読んでます。
この環境におおむね満足しているのだけど、電車の中で座れなかったときに本を読もうと思っても iPad を出すのはきびしいし、かといって iPhone だと PDF を等倍表示ではきびしいものがある。(Retinaならよめないことはないけど…) 拡大したら拡大したで、1ページを読むのにページ内をぐるぐるスクロールしつつ読まなくてはいけなくなり大変苦痛である。 文字を拡大したぶんだけページがずれていってくれるePubや青空文庫みたいな形式でなければiPhoneではきびしいなーと思っていました。bREADERをしるまでは!
この問題はがんばって紙の書籍をスキャンしてPDF化している現状が問題であって、出版社がePubみたいなので最初から電子出版してくれれば解決なんだからそれまで待とう…。 と普通の人は考えます。bREADERの作者はそういう発想はないらしく、力技でこの問題を解決しようとしているようです。
詳しくは先ほどリンクしたブログを参照していただくとして、手持ちのPDFで試してみた結果を紹介します。
まずこれがPDFをそのまま表示した時。(iPhoneの画面だとわかりにくいのでわざとコントロールを表示した画面にしています)
iPhoneの普通のPDFリーダーはみんなこんな感じに表示されてしまいます。Retinaディスプレイを搭載したiPhone4でやっとよめるというくらいですが、それでも小さくてつらいです。 つぎに bREADER がうまいことやってくれたあとの画面を見てみましょう
このようにまるでePubかと思うような拡大処理をしてくれてものすごく読みやすくなります。 そしてこのアプリの動作自体も BB2C を彷彿とさせる軽快さで、サクサク読み進められます。これはPDF化をがんばってる人でiPhoneのような小さいデバイスで本を読みたいと思っていた人には夢のようなアプリですね!
ただし現状ではPDFがそのまま自動で上のようなリフロー表示(というらしい)になってくれるわけではありません。ちょっとごにょっと作業をして上げる必要があります。
bREADER で PDF をリフロー表示に対応させる手順
リフロー表示を行うにはあらかじめMacかPCで書籍の文字情報を解析してメタ情報ファイルを作ってあげる必要があります。 作者がサイト上でそのためのbrcというツールを配布してくださっています。 が、そのツールはPDFに対応していませんのでまずはPDFをPNGとかなんか画像ファイルに変換してあげるという作業が必要になります。
- PDFを連番のPNG画像に変換
- brcでメタ情報ファイルを作成
- zipでかためてbREADERに転送
ってなかんじになります。
PDFを連番のPNG画像に変換
これをどうやろうかとおもいまして、最初に Preview.app を試してみたのですがどうも1ページずつしかPNGに保存できないっぽくあきらめ、OSX の機能をつかった pdf2png を適当にでっちあげた。
こいつを
$ gcc -framework Cocoa pdf2png.m -o pdf2png
とかでビルドしてあげて、
$ ./pdf2png foobar.pdf ./foobar
みたいにすると foobar.pdf を 0001.png みたいな連番の画像にして foobar ディレクトリに保存してくれると言う感じ。出力先のディレクトリはあらかじめ存在している必要があります。 PDFのスケーリングがよくわからなかったので適当に設定してあり、そこは誰か良い感じに書き直してくれればと…! あと CGBitmapContextCreate
の第一引数にNULLをわたしていてそこが OS X 10.6 依存です。それ以前のでうごかしたいときは malloc(w*h*4)
したの食わせとけばいいです。
brcでメタ情報ファイルを作成
先ほど生成した連番PNGが入っているディレクトリを brc コマンドに食わせる
$ brc ./foobar
これでそのディレクトリに d.brd というファイルが生成されていれば成功。
zipでかためてbREADERに転送
$ zip -0 foobar.zip *.png d.brd
みたいにして png と d.brd がふくまれた zip ファイルを作り、あとはそれを iTunes で bREADER のデーターフォルダに転送してあげればOK。簡単ですね。
まとめ
と、ここまで絶賛してきましたが現状では、ページ上部に常に出ている章の名前とか欄外に脚注なんかがあったりすると表示がおかしくなったりします。 とはいえそれでもiPhone上ではこの表示モードを使おうと言うくらいのレベルに既になっていると思います。これはほんとにすごい。
今後、bREADERもbrcも便利になっていくだろうし、brcはいらなくなる可能性もあるし、非常に楽しみですね!
bREADER 現バージョンの不満点をあげるとすればアプリ内に時計表示がどこにもなくアプリを落とさない限り時間がわからない、ということくらいでしょうか。 自炊派も、業者派も、iPhoneでPDF(書籍)を読む人すべてにおすすめなアプリです!
node.js C/C++ addons 入門
node.js のドキュメントを見ていたら C/C++ で簡単に拡張が書けそうだったので試してみた。
ドキュメントに載っている hello.cc をみてみると:
#include <v8.h>
using namespace v8;
extern "C" void
init (Handle<Object> target)
{
HandleScope scope;
target->Set(String::New("hello"), String::New("world"));
}
この extern "C" void init (Handle<Object> target)
というやつが、jsで require("hello")
したときに呼ばれる感じらしい。この関数だけは必ず実装する必要がある。
この中の Handle
や HandleScope
、String
といったものは全部 v8.h で定義された js 操作用のクラス。
Handle は JavaScript でのデータ全般(数値、文字列、オブジェクト、配列)を表すクラスで、init
関数には何も入っていない空のオブジェクト(ここでは target
)が渡される。
この例ではそのオブジェクトにたいして hello
というキーで world
という文字列を登録している。
なので、これを require すると、
$ node
> require("hello")
{ hello: 'world' }
こんな感じのオブジェクトが返るっていうわけです。簡単ですね。
アドオンのビルドには付属の node-waf
というコマンドを使う。これは waf に node.js アドオン用の機能を追加したものなのかな?
この node-waf は wscript という Makefile みたいな設定ファイルを用意してあげる必要がある。ドキュメントに載っているのはこんな感じ:
srcdir = '.'
blddir = 'build'
VERSION = '0.0.1'
def set_options(opt):
opt.tool_options('compiler_cxx')
def configure(conf):
conf.check_tool('compiler_cxx')
conf.check_tool('node_addon')
def build(bld):
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
obj.target = 'hello'
obj.source = 'hello.cc'
で、
$ node-waf configure build
とすることで ./build/default
に hello.node
がつくられる。
$ node
> require("./build/default/hello")
とかすればテストできます。
$ node-waf install
で、$NODE_PATH
で指定されたとこに(たぶん)インストールされ、そうすると単純に require("hello")
ができるようになる。
んでもって、C で書きたい! っていう場合は、
srcdir = '.'
blddir = 'build'
VERSION = '0.0.1'
def set_options(opt):
opt.tool_options('compiler_cxx')
opt.tool_options('compiler_cc')
def configure(conf):
conf.check_tool('compiler_cxx')
conf.check_tool('compiler_cc')
conf.check_tool('node_addon')
def build(bld):
c_obj = bld.new_task_gen('cc')
c_obj.name = 'c_obj'
c_obj.target = 'hello'
c_obj.source = 'foo.c bar.c'
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
obj.target = 'hello'
obj.source = 'hello.cc'
obj.add_objects = 'c_obj'
こんな感じの wscript を書けばいい模様。とはいえ、v8
自体が C++ 製なので、JavaScript とつなぐところは C++ が必要になる。
require 時になんかエスクポートしたりとかもっと良い感じのモジュールにするには
この辺読めば良さそうですね。
あと、C++ 側は、v8.h、node.h、を中心に include/node にあるヘッダファイルを見るといろいろわかりそう。IOやTimerをつかうときには libev の使い方も知っておく必要がある。 node.js には libev が組み込まれていて、EV_DEFAULT ループを本体がうごかしているから、そこにたいしておもむろに ev_io_start
とかしてあげるだけで拡張内で非同期IO使えます。
あとはいろんな拡張を参考に。Google コード検索で 「compiler_cxx node_addon」というので wscript を検索すると node.js 拡張だけが良い感じで引っかかってくるのでおすすめ。
OSX で tmpfs 的なことをする方法
MacBook Air 上で Test::mysqld
なテストを走らせていると SSD の寿命縮めそうだなーとおもって tmpfs 的なものが OS X にもないものかと検索したら hdid(8)
でできるとのこと。 man の一番下のほうに例が載っている。
$ hdid -nomount ram://128000
/dev/disk1
これでメモリから128000セクタ(1セクタ512バイトなので62.5MB)分とって仮想ディスクをつくり、
$ newfs_hfs /dev/disk1
Initialized /dev/rdisk1 as a 63 MB HFS Plus volume
HFSで初期化して、あとは普通に
$ mkdir /tmp/mnt
$ mount -t hfs /dev/disk1 /tmp/mnt
でマウントすればOK。
作ったディスクの破棄には
$ hdidutil detach /dev/disk1
"disk1" unmounted.
"disk1" ejected.
とかでいいらしい。
brew install mysql
なんか mysql_install_db --tmpdir=/tmp
とかしないとうまく行かなかったメモ。
$ brew install mysql
$ mysql_install_db --tmpdir=/tmp
$ mysql.server start
iPhoneバックグラウンドでのソケット監視のサンプル
iOS4 からアプリケーションのバックグラウンド動作がサポートされているが、それらはだいぶ制限されたものとなっていて、通常のアプリケーションはバックグラウンドでは最大10分程度しか生存することが出来ない。
それを回避するために Info.plist
に特殊なフラグをたてることで例外的に制限を解除することができるようになっている。そのフラグの種類は
- audio
- location
- voip
であり、それぞれバックグラウンド音声再生、バックグラウンド位置情報取得、バックグラウンドでのソケット監視(voipの着信監視用)に対応している。 そのうち voip だけ今まで扱ったことがなかったので挙動確認のためやってみた、というのが今回のお話。
まず書いてみたコード:
ソケットのハンドリングに libev を使っているので、ビルドを通すには $(HOME)/dev/iphone/lib/libev
に libev がインストールされている必要がある。 また、AppDelegate.m の #define HOST inet_addr("127.0.0.1")
という部分を自分のサーバーのIPになおしてあげるというのも必要。
んで、付属の server.pl が適当に作ったサーバーで、STDIN を読み込んで、入力があったらつながっているiPhoneにその内容を送信するというものになっている。 iPhoneアプリ側はバックグラウンド時にそのパケットを受け取ると受け取った内容をLocalNotificationで表示っていう感じ。
NSInputStream にたいして
[stream setProperty:NSStreamNetworkServiceTypeVoIP
forKey:NSStreamNetworkServiceType];
とかしてあげとくだけで、アプリがサスペンドしたときには OS 側がソケットの監視を代理でやってくれる。 で、そのソケットにたいして何かイベントが起こるとアプリを起こして処理を戻す。アプリ側は通常のソケット操作だけ実装しておけばいい。
このアプリのようにネットワーク周りはCで処理している場合、ファイルディスクリプタを NSStream にアップグレードする必要があってそこは若干めんどう。 Cocoa::EventLoop でもおなじことをしているのでその XS 部分からコピペしてきた。
Skype のバッテリー消費が半端ないのでこの機能にたいしてあまり良い印象はなかったのだが、実際にやってみるとバッテリー消費は思ったより気にならない(いまのところ。あまり検証はしていない)。 おそらく Skype は頻繁にパケットを送受信しているから、アプリが頻繁に起こされ、結果ものすごい勢いでバッテリーが減る、ということなのだろうか。
とにかく、なかなかおもしろい機能。VoIP アプリ以外でもつかえればなー。
ことえりのアイコンをATOKでつかう
セットアップメモに書き忘れたけど、ATOKのメニューバーアイコンがあまり好きじゃないのでことえりのアイコンをコピーしてきて使ってる。
まずバックアップとっておいて:
$ cd /Library/Input\ Methods/ATOK23.app/Contents/Resources
$ for f in Roman{,Button,Selected}.tif; do sudo cp $f ${f}.bak; done
$ for f in Hiragana{,Button,Selected}.tif; do sudo cp $f ${f}.bak; done
アイコンコピー:
$ sudo cp /System/Library/Input\ Methods/Kotoeri.app/Contents/Resources/FullRoman.tif RomanSelected.tif
$ sudo cp /System/Library/Input\ Methods/Kotoeri.app/Contents/Resources/FullRomanButton.tif RomanButton.tif
$ sudo cp /System/Library/Input\ Methods/Kotoeri.app/Contents/Resources/FullRomanSelected.tif Roman.tif
$ sudo cp /System/Library/Input\ Methods/Kotoeri.app/Contents/Resources/Hiragana.tif HiraganaSelected.tif
$ sudo cp /System/Library/Input\ Methods/Kotoeri.app/Contents/Resources/HiraganaButton.tif HiraganaButton.tif
$ sudo cp /System/Library/Input\ Methods/Kotoeri.app/Contents/Resources/HiraganaSelected.tif Hiragana.tif
英数とひらがな入力しか使わないのでその二つのアイコンしか置き換えていない。 あと、全角英数のアイコンをつかっているのはそっちのほうがかわいいから。
いまの悩みは ESET Cybersecurity のアイコンがかわいくないこと。
MacBook Air セットアップメモ
11インチのやつ、買いましたよ。よくあるセットアップメモを残す。 SSDの容量が心許ないので、バックアップからの復元はしないで新規で設定した。
OS 再インストール
まずやっぱ最初はこれ。目的は大文字小文字を区別するファイルシステムでパーティションを切りなおすということのみ。 デフォルトのファイルシステムではWindowsなどとおなじように大文字小文字を区別しない。そういう環境ではたとえば
#include <STDIO.h>
とか言うコードも通ってしまう。そういう気がつかないうちに仕込まれる各種トラブルの種は気持ち悪いよね。
注意点としてはAdobe製のソフトはそういうトラブルの種を抱えたコーディングをされているらしいものが多いようで、このファイルシステムにすると動作しないものが結構あるらしい。たとえばPhotoshopは最新版でもうごかないみたい。 (Lightroomは大丈夫)
ソフトウェアアップデート
とりあえず最新にしておく。
ESET Cybersecurity for Mac
アンチウィルスソフトはカルペルスキーを使ってきたけど、そんな満足してないってのと、NOD32でおなじみのESETからもMac用のソフトが出たみたいなのでとりあえずその試用版を試してみる。
1Password
パスワード管理ツール。データはいままでのMacからインポート。
Google Chrome
もはやこれがメインブラウザ。データまわりはChrome自体でGoogleアカウントいれれば同期が可能。楽ちん。
ATOK
Macにしてからずっと使ってるので。 これまた設定はいままでのMacからインポート。
KeyRemap4Macbook
キーバインド変更ツール。キーリピートを高速にしたり、ターミナルで左Commandをメタキーにしたりもろもろキー設定をこれで変更。必須ツール。
iTerm2
ターミナルはこれ。既存のMacから ~/Library/Preferences/com.googlecode.iterm2.plist
をコピーすれば設定引き継げる。
そこで使ってるフォント、CodingFontTobiも入れておく。
Quicksilver
ランチャー。キーボードトリガーでのアプリケーション切り替えがやっぱり便利。
- Cmd-[ = Emacs
- Cmd-] = iTerm
- Cmd-' = Google Chrome
って感じで割り当てている。
xcode (iOS SDK)
gcc など開発ツールのためにインストールは必須。現在は iPhone SDK が xcode も内包しているためiPhone開発をする場合はそれを入れればいい。
perlbrew
perlbrew で perl と cpanm をいれる
$ perlbrew install perl-5.12.2 --as 5.12.2
$ perlbrew install-cpanm
homebrew
homebrew でいろいろいれる
$ brew install wget
$ brew install git
$ brew install tig
$ brew install lv
などなど。homebrewは/usr/local
ではなく~/homebrew
で運用。いやー便利ですね!
Emacs
$ wget http://ftp.gnu.org/pub/gnu/emacs/emacs-23.2.tar.bz2
$ tar -jxvf emacs-23.2.tar.bz2
$ cd emacs-23.2
$ wget https://github.com/downloads/typester/emacs/feature-fullscreen.patch
$ wget https://github.com/downloads/typester/emacs/fix-shiftmodifier-with-ime.patch
$ patch -p1 < feature-fullscreen.patch
$ patch -p1 < fix-shiftmodifier-with-ime.patch
$ ./configure --with-ns --without-x
$ make install
で、nextstepディレクトリ以下にEmacs.appができるのでそれを/Applicationsに移動。設定とかは既存の環境からもってきた。
encfs
sshの秘密鍵とか、Config::Pitの設定ファイルとかはencfsで暗号化した領域に保存するようにしている。 これのインストールが大変面倒だったんだけど、homebrew のおかげですごい楽に入れられるようになった。
http://code.google.com/p/macfuse/ から MacFUSE の最新版をとってきてインストールしてそのあと、
$ brew install encfs
でOK。ただ、/usr/local 以外で運用している場合、依存している boost ライブラリ周りでエラーになるみたい。
$ brew install -vd encfs
としてあげるとエラーになったところでシェルに入ってくれるので、そこで自分でビルドする。 具体的には configure に --with-boost=
オプションをつけてあげるだけで良い。あとで Formula に追加してパッチ送ろうかと思う。
$ ./configure ...
$ make
$ make install
とかしたのちシェルを抜けると、homebrew が自動で symlink とか貼ってくれてインストール完了した状態になるみたい。これは便利。
ssh クライアント周り
keychain いれて、
$ brew install keychain
.ssh/config とかシェルの設定とかは既存の環境から持ってくる。
他
とりあえず最小構成はこんな感じであとは必要になったときにいろいろ入れていく。
Air のファーストインプレッション
CPU パワーが必要になる場面ではやはり非力だけど、それ以外の場面では SSD のおかげかすごい快適。 あとはキーボード、MacBookと比べるとなんというか作りが悪くてタイプ感もいまいち。Escの押しにくさは異常。
とはいえ軽くて小さくて、バッテリーがすごい持つのはやっぱり良いねー。 CPU は弱いけど全体的なパフォーマンスは良いから自分的にはメインマシンになっていくんだろうな。いままでのMacBookどしよっかな。。
CoffeeScript いいねー
node.js とかが盛り上がっていてもまったく興味をそそられなかったのは、JavaScript 自体があんまり好きじゃないってのがあった。
- function () {} とかのシンタックスシュガーがない
- レキシカルスコープない
とかもろもろ。基本的に美しくコードがかけない言語だと思ってた。
っていうのを一気に解決してくれるのが CoffeeScript。
この言語は専用のインタラプタで実行することも出来るけど、 1:1 で JavaScript に変換できるので、変換後のコードは普通の JavaScript インタラプタで実行することができる。 なので普通にWebサイトでつかってるjsもCoffeeScriptで書いて変換したのを使うってことができる。
具体的なシンタックスについては上記のサイトをみてもらうとして、僕が不満だった部分はほぼなくなっている。インデントによるブロックは僕はあまり好きじゃないけど、Emacs用のcoffee-modeもすでにあるのでたいした問題じゃない。
js はもうこれでしか書かないなー。あんまり書く機会ないけどさ。