2014年11月14日金曜日

proxygenビルド

今話題のproxygenをビルドしてみました。

ちなみに、proxygenってのは、たとえばSourceForgeの記事によれば
米Facebookは11月5日、C++向けのHTTPライブラリ「Proxygen」を発表した。サーバーやクライアント向けコードが含まれており、HTTPを利用したC++プログラムのやりとりを改善し、HTTPを利用するアプリケーションの構築を容易にするという。
Proxygenは、HTTPライブラリとシンプルなWebサーバーを組み合わせたソフトウェアフレームワークで、Facebook社内においてHTTPサーバーやプロキシ、クライアントなどを構築する際に利用されている。Proxygenの公開の目的として、高性能なHTTPサービスの開発と実装を支援するとしている。サーバーとクライアントコードを含み、既存のアプリケーションと容易に統合できる高性能のHTTPフレームワークを目指しているという。
という、素敵なサムシング なわけです。GitHubで公開されているソースコードをざっと見たところ、綺麗な構造で、期待が高まります。

で、ビルドです。基本的にhttps://github.com/facebook/proxygenに書いてあるのに従えばOK。ubuntu環境を用意するところから始めました。"Support for Mac OSX is incomplete."とか書いてありますけど、依存するfollyやらfbthriftやらをどうやって用意するんだ、という問題があるので、まあ、現時点ではちと難しいんではないでしょうか(homebrewでfolly入れてみなよ、みたいなことがGoogle Groupの方で書かれていましたが…)。

1. OSインストール編

1-1. まず、VMware Fusion 6でインスタンスを作りました。ubuntu 64ビット用テンプレートを使って 、ディスク20GB、メモリ2560MB(2.5GB)、1 CPU(古いiMacなもんで)でセットアップ。なお、メモリは最低2GB必要とのこと。
1-2. http://www.ubuntu.com/download/desktop からubuntu 14.04.01 desktopのisoファイルをダウンロード。14.04.xxだったら何でもいいんじゃないでしょうかね。
1-3. ダウンロードしてきたisoファイルをVMゲストの光学ドライブに接続して起動。言われるがままにインストール。OSのアップデート、再起動など勧められるがままに従う。

2. ubuntu上での準備編

$ sudo apt-get install git
$ mkdir git && cd git
$ git clone https://github.com/facebook/proxygen.git

3. ライブラリビルド編

$ cd proxygen
$ export SRC_HEAD=`pwd`   ##これ、後で使う。
$ cd proxygen
$ ./deps.sh

4. サンプルサーバビルド編

$ cd httpserver/samples/echo
$ g++ -std=c++11 -o my_echo EchoServer.cpp EchoHandler.cpp -lproxygenhttpserver -lfolly -lglog -lgflags -pthread -I$SRC_HEAD -I$SRC_HEAD/proxygen/fbthrift/thrift/folly -I$SRC_HEAD/proxygen/fbthrift -L$SRC_HEAD/proxygen/httpserver/.libs -L$SRC_HEAD/proxygen/fbthrift/thrift/folly/folly/.libs

## https://github.com/facebook/proxygenに書いてあるまんま(g++ -std=c++11 -o my_echo EchoServer.cpp EchoHandler.cpp -lproxygenhttpserver -lfolly -lglog -lgflags -pthread)だとエラーが出るので、-Iと-Lを指定。

5. サンプルサーバ動作確認編

$ sudo apt-get install curl
$ curl -v http://localhost:11000/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 11000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:11000
> Accept: */*
> 
< HTTP/1.1 200 OK
< Request-Number: 1
< Date: Fri, 14 Nov 2014 13:13:31 GMT
< Connection: keep-alive
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

以上。何も戸惑う箇所はありません。ライブラリのビルドに時間がかかるものの、手間もかかりません。

そして、気になるHTTP/2。
Proxygen: Facebook's C++ HTTP LibrariesThis project comprises the core C++ HTTP abstractions used at Facebook. Internally, it is used as the basis for building many HTTP servers, proxies, and clients. This release focuses on the common HTTP abstractions and our simple HTTPServer framework. Future releases will provide simple client APIs as well. The framework supports HTTP/1.1, SPDY/3, and SPDY/3.1. HTTP/2 support is in progress. The goal is to provide a simple, performant, and modern C++ HTTP library.
FacebookがHTTP/2対応版を出してくるのを 待つか(それって、でも、来年2月とかになるのかな)、独自にSPDY用のコードをごにょごにょしてHTTP/2対応させてみるか…。はてさて。

2014年11月8日土曜日

SKProductsRequestでハマった。抜け出したけど。

前回に引き続き、iOSアプリの開発ネタです。初めてっていうのは、いろいろな問題にぶち当たりますな。

原因不明でアプリがクラッシュすることが何度かあり、どうやら、アプリ内課金の関係で、Appleのサーバに問い合わせに行っている最中に、「遅い!もういいよ!」みたいな感じで画面遷移(具体的には、ナビゲーションの「戻る」を押す)をしてしまうと、ごく稀にこの問題が発生する、ということまで絞り込むことができた。
ごく稀にというのが、まあ、厄介なところ。

ちなみに、アプリがクラッシュした時のXcode(ちなみにバージョンは6.1)の表示はこんな感じ。

コードがこれ↓


スタックがこれ↓


これだけだと、さっぱり、意味がわからない。

"Enqueued from com.apple.root.default-qos"と"EXC_BAD_ACCESS"とかでググっても、なかなかこれというのが見つからないし、なんと言っても、"Enqueued from com.apple.root.default-qos"と"SKProductsRequest"の組み合わせで検索結果がゼロというのに参った。
SKProductsRequestのstartメソッド呼ばない限りクラッシュしないので、この関係だってことは間違い無いと思うんだけど…。

そして、結局、自分で、何が原因なのかわかりました。ので、情報共有です。

こんなコードを書いていたんですよ。どこかで見たコードのコピペですけど。
- (void)startRequest
{
    SKProductsRequest* productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];
    productRequest.delegate = self;
    [productRequest start];
}
これだと、サーバからのレスポンスを受け取った時に、selfがdeallocateされてしまった後だと、まずいことになるんですよね。productRequestの後始末が考慮されていない。

というわけで、僕が採った解決法はこれ。
まず、ヘッダファイルでこんな宣言。
@property (nonatomic, strong) SKProductsRequest *productRequest;
そして、本体コードでは、
- (void)startRequest
{
    _productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];
    _productRequest.delegate = self;
    [_productRequest start];
}


これに加えて、viewWillDisappearで、delegateを無効化してやる処理を加えました。
- (void)viewWillDisappear:(BOOL)animated { _productRequest.delegate = nil; [[SKPaymentQueue defaultQueue] removeTransactionObserver:self]; }


そしたら、(今のところ?)ばっちり、っぽいです。なんか自信なさげな書き方で恐縮ですが。いかんせん、稀にしか起こらない問題なので断言が難しいのです。

iOSアプリでの各国語対応。なるほど、そうだったのか。

久しぶりにブログ書きます。
ちなみに、会社で、仕事の一環として、時々英語でブログ書かされたりしてるんですが、なんていうか、仕事で書くのって気が重いですね。ま、それはさておき。

実は、内緒にしていましたが、ここ最近、iOSアプリを作ってたんです。初めての。
それで、一応、大した手間じゃないので、各国語対応させてみよう、と、ということで、ベース言語(実際は英語で記述)、それに加えて日本語用の文字列ファイルを用意しました。

今まで、「iPhoneの使用言語」として日本語と英語を設定してそれぞれでの表示を試してみては、ふむふむ、うまくいっておるわい、と思っていたのですが、先ほど、iPhoneの使用言語をイタリア語に設定してみると、期待に反してなんと日本語の文字列が表示されてしまうではないですか。
うーむ、何か間違った設定をしているのだろうかと軽くググってみたけれども、うまく期待する答えにたどり着けない。

結局、自分で原因がわかって全て丸く収まりました。
こういうことです。↓

設定言語を、英語⇒日本語⇒イタリア語、という順で変更すると、「使用する言語の優先順位」(設定アプリの「一般>言語と地域」の中にある)が

  1. イタリア語
  2. 日本語
  3. 英語

となってしまうんですね。自動で。

この状況で、アプリがイタリア語にローカライズされていないからアプリのベース言語(つまり英語)が選ばれるかと予想していたのですが、そうではなく、英語よりも高い優先度で日本語を使う設定に(iOSの判断によって)なっているので、日本語が表示される、と。そういうことでした。
つまり、「iPhoneの使用言語」よりも「使用する言語の優先順位」のほうが意味がある、と。

ここで、仮に、日本語(と英語の両方)が「使用する言語の優先順位」に含まれていない場合、ベース言語(つまり英語)が表示されるんでしょうね。
試してみたいのですが、「使用する言語の優先順位」は手動で編集できないので、残念です。

ところで15年ぐらい前はローカライズを"l10n"、国際化を"i18n"とか表記してたんですけど、これって今の若い人にも通じるんでしょうかね。逆に、今では別の表記をしているなら、それを知りたい。
ちなみに由来は
"localization"⇒"lxxxxxxxxxxn"(LとNの間に10文字)⇒"l10n"
"internationalization"⇒"ixxxxxxxxxxxxxxxxxxn"(IとNの間に18文字)⇒"i18n"
なので、表記が"l10n"や"i18n"であっても、「えるじゅーえぬ」「あいじゅーはちえぬ」などと読むことはなく、よみがなとしてはやはり「ろーからいぜーしょん」「いんたーなしょならいぜーしょん」でした。少なくとも僕の周辺では。