MonoTouch製Appが生成したクラッシュログのシンボルを解決する
Appがクラッシュした時に生成されるクラッシュログ。この中身は関数アドレスが書いてあるだけでとても読み取りにくい。本家Appならばオーガナイザでアドレスのシンボル解決を自動的にやってくれるのだが、MonoTouch製Appの場合は手動で行う必要がある。
下準備
シンボル解決コマンドsymbolicatorに対する環境変数が必要なのでホームディレクトリの.bash_profileに以下を追加する
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer/
そしてコマンドへのパスがとても長い。
Xcode v4.5.2の時点でパスは/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resourcesになる。いちいちこれを入力するのは面倒なので.bash_profileに以下を追加した。
export PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources:$PATH
Xcode v5では/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrashになる。
dSYM情報の準備
symbolicatorはdSYM情報を必要とする。本家Appの場合は~/Library/Developer/Xcode... なディレクトリに自動生成されている。
MonoTouchの場合はビルド出力先に{プロジェクト名}.app.dSYMという名前のフォルダが生成されている。しかし実はこのままでは必要なファイルが足りずシンボル解決できない。これは{プロジェクト名}.appフォルダ以下のファイルをコピーするだけで解決する。
cd {ビルド先} cp -nR {プロジェクト名}.app/* {プロジェクト名}.app.dSYM
※ビルドするとdSYMフォルダは削除されるのでその度この作業が必要になる。
symbolicatorの実行
symbolicatorを実行する
symbolicator クラッシュログファイル名 {プロジェクト名}.app.dSYM
これでようやくシンボルが解決される。
追記 2014/03/11 14:10
Xcode5でのパスを追記
キーボードのReturnキー(の部分)が押された時の処理
キーボードのReturnキーの部分が押された時の処理はShouldReturnデリゲートを使う。
- 次のフィールドにカーソルを移動する場合
UITextField text1; UITextField text2; text1.ShouldReturn = delegate { // BecomeFirstResponderメソッドで移動させる text2.BecomeFirstResponder (); return true; };
- キーボードを閉じたい場合
UITextField text1; text1.ShouldReturn = delegate { // ResignFirstResponderメソッドでレスポンダを解除する text1.ResignFirstResponder (); return true; };
NSUserDefaultsへのアクセス
Settings.Bundleで定義された設定を操作してみた。
取得
NSUserDefaults prefs = NSUserDefaults.StandardUserDefaults; string testValue = prefs.StringForKey("testKey");
設定
NSUserDefaults prefs = NSUserDefaults.StandardUserDefaults; prefs.SetString("1", "testKey"); prefs.Synchronize(); // この行がないと保存されない
完全初期化(KeyChainの内容も消えてしまう?)
NSUserDefaults prefs = NSUserDefaults.StandardUserDefaults; prefs.RemovePersistentDomain(NSBundle.MainBundle.BundleIdentifier);
KeyChainを使ってみた
入力されたパスワードを安全に保存するためにKeyChainを使ってみた。
最初保存したレコードを読み出せず苦労したがAccessible属性を設定することで解決した。この属性は必須なのだろうか。
using MonoTouch.Security; private string LoadPassword (string userCode) { if (string.IsNullOrWhiteSpace (userCode)) return ""; using (SecRecord query = CreateSecRecord(userCode)) { SecStatusCode res; using (SecRecord rec = SecKeyChain.QueryAsRecord (query, out res)) { if (res == SecStatusCode.Success && rec.Generic != null) { return NSString.FromData (rec.Generic, NSStringEncoding.ASCIIStringEncoding); } else { return ""; } } } } private void SavePassword (string userCode, string password) { if (string.IsNullOrWhiteSpace (userCode)) return; RemovePassword (userCode); using (SecRecord newRecord = CreateSecRecord(userCode)) { using (NSData pass = NSData.FromString(password)) { newRecord.Generic = pass; // ↓この行が無いと保存したレコードを読み取ることが出来なかった。 newRecord.Accessible = SecAccessible.Always; SecKeyChain.Add (newRecord); } } } private void RemovePassword (string userCode) { if (string.IsNullOrWhiteSpace (userCode)) return; using (SecRecord query = CreateSecRecord(userCode)) { SecKeyChain.Remove (query); } } private SecRecord CreateSecRecord (string userCode) { // SecKind.GenericPasswordの時、AccountとServiceの2つでもってレコードがユニークになる SecRecord rec = new SecRecord (SecKind.GenericPassword); rec.Account = userCode.ToLower (); rec.Service = "MyService"; return rec; }
コンパイル時のワーニングを無視させる
こちらの都合で使わないけど引数を宣言する場合がある。
この時MonoDevelop(というかコンパイラ)が律儀にワーニングを出してくれるができれば無視して欲しい。
で、こんな時はpragmaで制御できるのでその備忘録
// variable declared but not used. #pragma warning disable 0168 int a; #pragma warning restore 0168 // variable assigned but not used. #pragma warning disable 0219 int a = 0; #pragma warning restore 0219 // private field assigned but not used. #pragma warning disable 0414 #pragma warning restore 0414
CATextLayerのRetina対応
RetinaなiPadでCATextLayerの文字がぼやけているのに気づいた。
UILabel等はくっきり表示されている。
調べるとCALayerのContentsScaleを適切に変更しなければならなかった。
CATextLayer textLayer = new CATextLayer();
textLayer.ContentsScale = UIScreen.MainScreen.Scale;
これで綺麗に表示されるようになった。
画像をタイル上に並べて描画してみた
画像をタイル上に並べて描画する必要があったのでMonoTouchでコードを書いてみた。
ここでの描画先はCALayer。
アニメーションは必要無かったのでOFFにしてある。
using MonoTouch.CoreAnimation; using MonoTouch.CoreGraphics; private CALayer layer; private CGImage image private float[] components = new float[]{1.0f}; private CGAffineTransform matrix = new CGAffineTransform (1, 0, 0, 1, 0, 0); // CGImageを作ったらこのメソッドを呼ぶ private void Draw() { CATransaction.Begin (); CATransaction.AnimationDuration = 0; float imageWidth = image.Width; float imageHeight = image.Height; RectangleF rect = new RectangleF (0, 0, imageWidth, imageHeight); using (CGPattern pattern = new CGPattern (rect, matrix, imageWidth, imageHeight, CGPatternTiling.ConstantSpacing, true, DrawTiledImage)) { using (CGColorSpace space = CGColorSpace.CreatePattern (null)) { using (CGColor color = new CGColor (space, pattern, components)) { layer.BackgroundColor = color; } } } CATransaction.Commit (); } private void DrawTiledImage (CGContext context) { float imageWidth = image.Width; float imageHeight = image.Height; context.DrawImage (new RectangleF (0, 0, imageWidth, imageHeight), image); }