Google AnalyticsにはiOS版があり、アプリ内の画面操作に仮想的なURLを割り当てることで、Webサイトと同じように”アクセス解析”ができる。 インストール 公式サイトからSDKをダウンロードして展開、Libraryディレクトリの中に入っている GANTracker.h と libGoogleAnalytics.a を自分のプロジェクトにコピーする(もう一つの libGoogleAnalytics_NoThumb.a はThumb命令を無効にしたプロジェクトで使用するもの。標準のプロジェクトでは必要ない)。 次にXcodeでプロジェクトファイルを開き「TARGETS」でアプリを選択、「Build Phases」「Link Binary With Libraries」を開いて、「+」ボタンを押してCFNetworkフレームワークとlibsqlite3.0.dylibを追加する。ちなみに libGoogleAnalytics.a はプロジェクトにコピーした段階で追加されているはず。 使用方法 公式サイトのドキュメントを読めばわかる通り、トラッキングの概念や使い方はWeb用のGoogle Analyticsとほぼ同じなので、迷うことはないだろう。ただトラッキング用のメソッドを実行してもすぐにはGoogleサーバーにデータが送られず、一定の時間(初期化時に dispatchPeriod として指定する)ごとにまとめて送られるという動作が異なる。 あと GANTracker.h のコメントにはサイトに書いていない有用な情報が含まれているので、そちらもチェックした方が良い。下記の便利マクロに使っている知識もそこから得た。 便利マクロ http://d.hatena.ne.jp/ninjinkun/20110115/1295074900 ここで紹介されているマクロが便利なので改造して使わせていただいている。自分で追加・変更した内容は以下の通り。 初期化マクロ GA_INIT_TRACKER(ACCOUNT, PERIOD, DELEGATE) GAアカウントと送信間隔とdelegateの設定を行う、初期化用のマクロ。定義は下のようになる。 #define __GA_INIT_TRACKER(ACCOUNT, PERIOD, DELEGATE) \ [[GANTracker sharedTracker] startTrackerWithAccountID:ACCOUNT \ dispatchPeriod:PERIOD \ delegate:DELEGATE]; #ifdef DEBUG #define GA_INIT_TRACKER(ACCOUNT, PERIOD, DELEGATE) { \ __GA_INIT_TRACKER(ACCOUNT, [...]
UITableViewのスクロール位置をView再読み込み時に復帰する方法
メモリ警告によってViewが破棄された場合、UITableViewのスクロール位置はリセットされてしまう。再度Viewが読み込まれたときに元の位置に戻すには、破棄される直前のスクロール位置を覚えておく必要がある。だいたい次のようなコードになる: ScrollTestViewController.h #import <UIKit/UIKit.h> /* UITableViewControllerではなくUIViewControllerを使っている理由は後述 */ @interface ScrollTestViewController : UIViewController<UITableViewDataSource, UITableViewDelegate> { /* UITableViewはIBOutletでretainする想定 */ UITableView *_tableView; /* Viewが破棄される直前のスクロール位置 */ CGPoint lastScrollOffset; } @property (nonatomic, retain) IBOutlet UITableView *tableView; @end ScrollTestViewController.m @implementation ScrollTestViewController @synthesize tableView = _tableView; – (void)viewDidLoad { [super viewDidLoad]; /* reloadDataでデータを読み込んだ後に 記録されているスクロール位置を復帰 初期化直後は{0,0}で初期化されているので影響はない */ [...]
muninの集計負荷をtmpfsで低減する(集計時間の短縮も)
リソース監視ツールのmuninは手軽に使えて素晴らしいのだが、監視対象のサーバが増えてくると集計サーバの負荷(主にディスクI/O)が馬鹿にならなくなってくる。そこでこちらのサイトを参考にして、データファイルとHTMLファイルをtmpfs上に移動してみた。 環境 CentOS 6 munin-1.4.5 (EPELリポジトリからインストール) メモリ: 8GB データファイルとHTMLファイルの場所は /etc/munin/munin.conf の dbdir と htmldir で変更できる…はずなのだが、プラグインの中には /var/lib/munin のパスを決め打ちにしているものがあるらしく、dbdir を変更するとうまく動かなかった。仕方がないのでHTMLファイルを /var/lib/munin 以下に移動して、tmpfs を /var/lib/munin にマウントすることにした。 まずHTMLファイルを移動する。以下の操作はroot権限で、muninの集計を停止してから行う。 muninの集計を止めるには、munin-cronの実行権限を外すか、/etc/cron.d/munin を編集してcronによる定期実行を止める。 # munin-cronの停止 chmod ugo-x /usr/bin/munin-cron # 念のためmuninプロセスが動いていないか確認しておく ps aux | grep munin 次にHTMLファイルをディレクトリごとコピー、そのパスにあわせてmuninとApacheの設定を変更する。 cp -a /var/www/html/munin/ /var/lib/munin/html vim /etc/munin/munin.conf # htmldir /var/lib/munin/html vim /etc/httpd/conf.d/munin.conf # 私の環境ではcgiディレクトリは [...]
Rails3のi18n機能および日本語化
必要な情報はRails Guidesの中の以下のページにまとまっている。 Rails Internationalization (I18n) API I18n.translate メソッドを使って「抽象化されたキー」から、ロケールに基づく「自然言語」に変換する。いきなり英語で文章を考えなくて済む分、日本人には取っ付きやすいかもしれない。同様に、 I18n.localize メソッドで日付や時刻のフォーマットを行う。 それぞれ I18n.t, I18n.l という省略形が用意されている。さらに多くのコンテクストでは t, l というショートカットメソッドが使える。 Railsフレームワーク自体もこのメカニズムで国際化されているため、各コンポーネントが要求するキーに対して適切な翻訳を用意することで、フレームワーク全体が翻訳される。 キーは階層化することができる。またRailsが要求するキーも、コンポーネントごとに階層化されている(例: activerecord.errors.messages.record_invalid など) 標準のメッセージファイルにおいては、YAML(.yml)またはRuby(.rb)のハッシュを使って、キーの階層をそのまま再現する。 階層の上位部分をスコープ(scope)と呼ぶが、どこまでがスコープかという区別は明確ではない。 I18n.locale= で使用するロケールを設定する。 使用するロケールの決定方法について。Web”サイト”においてはURL中にロケールを含めるべきである。一方で、Web”アプリケーション/サービス”においてはAcceptヘッダやDBに保存した個人設定を使うことも有効である。 フレームワーク全体の日本語化 とにかく手っ取り早く”日本語化”をしたいという場合は https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml を config/locales/ に放り込んで、config/application.rb に config.i18n.default_locale = :ja と書いておけば、フレームワークが自動で出力する各種メッセージは日本語になる。ただ標準ではエラーメッセージのフィールド名の後に空白が入るので、errors.formatを次のように修正しておいた方が良いかもしれない(好みの問題だが)。 — ja.yml.orig 2011-06-22 22:38:21.000000000 +0900 +++ ja.yml 2011-06-22 22:38:33.000000000 +0900 @@ -188,7 +188,7 @@ submit: “保存する” errors: – [...]
Rails3+Rspecでファイルアップロードのテストをする
標準のTest::Unitに含まれている fixture_file_upload はrspec-railsに含まれていないので、自分でspec_helper.rbに追加する。 def fixture_file_upload(path, type) Rack::Test::UploadedFile.new(fixture_path + path, type) end 標準の fixture_file_upload 同様、Rack::Test::UploadedFile のインスタンスを返すだけ。 テストに使用するファイルは spec/fixtures/ 以下に置く。例えば spec/fixtures/files/dummy.txt を使用するには fixture_file_upload("/files/dummy.txt", "text/plain")
Rails3でjQueryを使う場合の注意点
新規プロジェクトの作成時に -J オプションを付けてprototype.jsの読み込みをスキップした場合、config/application.rb の中で以下の設定が有効になる。 config.action_view.javascript_expansions[:defaults] = %w() あとからjQueryを使うためにjquery-railsをインストールしたとき、この設定が有効なままだとビューの中で javascript_include_tag :defaults を書いてもjquery.js等が読み込まれない。 忘れずにコメントアウトしておく(コメントアウトすることで、jquery-railsが自動設定した値が有効になる)。 #config.action_view.javascript_expansions[:defaults] = %w() あるいは、jqueryとapplication.js以外に常時読み込みたい.jsファイルがあるのなら、+= で配列に追加しておく。 config.action_view.javascript_expansions[:defaults]+= %w(mylib.js otherlib.js)
CouchDB 1.1.0をMac OS Xにインストール
公式WikiではHomebrewによるインストールが推奨されているので、brewコマンドで普通にインストールする。 brew install couchdb Erlang R14B03 と couchdb1.1.0 がインストールされた。 この後Wikiの手順では 手動でcouchdbユーザとcouchdbグループを作る /usr/local 以下の所有者とパーミッションを変更して、/Library/LaunchDaemons/ に起動スクリプトをコピー sudoを使ってcouchdbを起動/停止するaliasを設定 となっているが、brewで管理している /usr/local 以下の所有者を変更するのはちょっと嫌だし、どうせ開発用に使うだけだし、そもそもインストール後に表示されるメッセージには If this is your first install, automatically load on login with: mkdir -p ~/Library/LaunchAgents cp /usr/local/Cellar/couchdb/1.1.0/Library/LaunchDaemons/org.apache.couchdb.plist ~/Library/LaunchAgents/ launchctl load -w ~/Library/LaunchAgents/org.apache.couchdb.plist と書いてあったので、起動スクリプトは ~/Library/LaunchAgents/ にコピーして、次のようなエイリアスを設定することにした。 alias restart_couch=’launchctl stop org.apache.couchdb’ alias start_couch=’launchctl load -w ~/Library/LaunchAgents/org.apache.couchdb.plist’ alias stop_couch=’launchctl unload ~/Library/LaunchAgents/org.apache.couchdb.plist’ 起動したり停止したりするのに [...]
IBOutletで接続した変数はreleaseする必要があるのか
iOSとMac OS Xとで結論が異なる。OS X開発経験者が書いた書籍やWebサイトはこの違いを考慮していない場合があるので注意が必要。 …… IBOutletの宣言はインスタンス変数にもプロパティにも書くことができる。 @interface IBOutletTestAppDelegate : NSObject <UIApplicationDelegate> { UILabel *_retainLabel; UILabel *_assignLabel; IBOutlet UILabel *_variableLabel; } @property (nonatomic, retain) IBOutlet UILabel *retainLabel; @property (nonatomic, assign) IBOutlet UILabel *assignLabel; @end iOSの場合、IBOutletをどちらに書いても、アウトレットの接続は常にNSObject(NSKeyValueCoding)の setValue:forKey: メソッドを使って行われる。このメソッドは(1)まず対応するプロパティのsetterを探して、(2)見つからない場合はインスタンス変数に代入してretainカウントを1増やす。よってassignプロパティを使うのでない限り、アウトレットで接続した変数は常にreleaseしなければならない。 この動作は setValue:forKey: をオーバーライドしてみれば確認できる。 – (void)setValue:(id)value forKey:(NSString *)key { //retainのプロパティとインスタンス変数の場合は //retainCountが1増える NSLog(@"[before setValue] %@ %d", key, [value retainCount]); [super [...]
External Objectを使ってIBOutletからNibファイル外のオブジェクトに接続する
External Objectを使うことで、Nibファイル外のオブジェクトに対してIBOutletで接続できる。ちなみにこの機能はMac OS Xでは使用できず、iOSでのみ使える。 …… たとえばあるカスタムビューからモデルのインスタンスを参照したいとする。このビューでは常にモデルの状態を反映した表示を行いたいので、インスタンス変数にモデルへの参照を保持しておきたい。 ビューの生成・初期化をプログラム中で行うなら、initWithModel:のような初期化子を用意しておけば済む。しかしビューをNibファイルから生成する場合は初期化時にモデルを参照することができない(モデルは普通Nibファイルで生成しないので)。仕方がないのでモデルのインスタンスだけはUIViewControllerのviewDidLoadでビューに代入して、専用の初期化メソッドを呼び出すなどするしかない。これは非常に面倒だし忘れやすい。 – (void)viewDidLoad { [super viewDidLoad]; //プロパティに代入してから表示を更新。面倒くさい _customView.model = _model; [_customView update]; } そこでExternal Objectの出番である。このオブジェクトはNibファイル外の既成のオブジェクトに対するPlaceholderとして機能する。ちょうど自由な名前をつけられる「File’s Owner」だと考えれば良い。 使い方は次のようになる。まずカスタムビューの.hファイルで、モデルに対するOutletを設定しておく。 @interface CustomView : UITableViewCell { Model *_model; } @property (nonatomic, retain) IBOutlet Model *model; モデルを使った初期化処理は、その他の初期化処理と同じくawakeFromNibの中に書くことができる。 – (void)awakeFromNib { [super awakeFromNib]; //モデルのデータを使って表示を初期化する _textField.text = _model.name; } 続いてxibファイルを開いてExternal Objectの設定を行う。Libraryの中から「External Object」を選んでObjectsの欄にドラッグアンドドロップすると、Placeholdersの欄にオブジェクトが追加される。 追加されたオブジェクトを選択してIdentity [...]
Eee PC 1015PEMにUbuntu 10.10インストール(無線LANの問題解消)
追記(11.10用) 下記の設定を行った状態で11.10にアップデートしたら無線LANが認識されなくなった。/etc/modprobe.d/blacklist.conf で次の1行をコメントアウトして再起動したら復活した。 #blacklist rt2800pci … ASUSのネットブックEee PC 1015PEMを買った。標準で入っていたWindows 7 Starterを使う気はさらさらなかったので、リカバリ領域をUSBメモリにバックアップした後、HDD丸ごと使ってUbuntuをインストールした。もうすぐ11.04が出るのだが、とりあえず10.10を選択。 1015PEMには光学ドライブはないのでインストールはUSBメモリから。手順はUbuntu公式の「Netbook」のダウンロードページに書いてある。まずWindowsでUniversal USB Installerをダウンロードして、次にUbuntu Japanese Teamのサイトから10.10 Desktop 日本語Remixのisoイメージを落とし、Universal USB Installerを起動してUSBメモリに書き込んだ。 あとはUSBメモリを刺して起動時にBIOSでboot deviceを指定、画面に従ってインストールするだけ。ちなみにこのときUSBメモリから直接起動して動作をテストすることもできるが、その動作は実際にインストールしたときの動作とは必ずしも一致しないことに注意。私の場合、この方法で起動すると無線LANが全く繋がらなかったり、日本語入力が使えなかったりした。 インストール自体は30分ほどで終了。早速無線LANに繋いでソフトウェアアップデートをかけるが、どうもネットワークの速度が遅い。pingを打ってみると30%〜ほどのパケットが欠落している。有線LANに繋ぎ変えると正常なので、明らかに無線LANがおかしい。Windows 7で使ったときには正常だったから、問題は無線LANアダプタのドライバだろうと当たりをつけて調査開始。 結論から言うと、次の操作を行うことで解消した: /etc/modprobe.d/blacklist.conf に以下の内容を追記して、マシンを再起動。 blacklist rt2800pci blacklist rt2800lib blacklist rt2800usb blacklist rt2x00usb blacklist rt2x00pci blacklist rt2x00lib 問題を解消するにあたってはこのページが非常に役に立った。 Ubuntu 10.04 LTS でEeePC 1015PEMで無線LANを使う(Ralink RT3090) – Ubuntuスタイル まずlspciコマンドを実行してみると、確かにRaLink社のRT3090というアダプタが搭載されているのが確認できた。そこでRaLink社のサイトを見に行ったところ、Linux用のドライバが配布されていたので、早速「RT3090」用のファイルをダウンロードした。zipファイルを展開して中を見てみると、このドライバは「RT2860STA」という名称だと判明した。 ここでインストールする前に念のため lsmod | grep rt で現在読み込まれているドライバを確認してみたところ、既にrt2860staは読み込まれている、つまりUbuntu [...]