plistを読み込み、保存する方法

http://ameblo.jp/growing-flutter/entry-10407102031.html
http://d.hatena.ne.jp/It_lives_vainly/20090311/1236745568

データパーシステンスを実現する方法で、一番シンプルなのがplist(XMLフォーマット)で保存する方法。でも、シミュレータでは問題なく動作するのに、実機だとうまく動作しないというトラブルに悩まされました。どうやらwriteToFileできてないようなんですね…。

で、結局わかったのが、アプリケーションディレクトリ下のDocumentsディレクトリに保存するように記述しないと実機では保存できないということ。

- (NSString *)dataFilePath {
	NSString* a_home_dir = NSHomeDirectory();
	NSString* a_doc_dir = [a_home_dir stringByAppendingPathComponent:@"Documents"];
	NSString* a_path = [a_doc_dir stringByAppendingPathComponent:@"hoge.plist"];
	return a_path;
}

簡単に目的のパスを得られるよう、パスの文字列を戻すメソッドを作成。あとは実際にwriteToFileやinitWithContentsOfFileするときに引数に[self dataFilePath]を与えてやるようにしました。

実機インストール

http://kentaro-shimizu.com/lecture/iphone/step3.html

全体の流れはこのサイトの通りに進めたらできました。
でも、App IDの設定とプロビジョニングファイルの選択に関しては、

http://d.hatena.ne.jp/It_lives_vainly/20090204/1233741543
http://d.hatena.ne.jp/veck/20090608/p2

こっちのサイトが参考になりました。App IDって、アプリごとに作成しなくてもここに書いてある方法で作成すればワイルドカードとして使用できるようです。

メモリリーク

わがブログのタイトルどおり、メモリリークしまくってます。。
メモリ管理はallocやretainをしたら必ずrelease、ぐらい単純な意識で行ってました。でもそれだけじゃやっぱりだめっぽい。最近メモリ関連のエラーでアプリが落ちまくりです。突如落ちてしまうときには、開放済みの変数にアクセスしてしまっているケースがほとんどのようですね〜。かといってやたらとretainしまくるってのもメモリリークにつながるし…、ここはちゃんとメモリ管理を学ぶいい機会ですな。

http://www.atmarkit.co.jp/fcoding/articles/objc/08/objc08a.html
http://www.atmarkit.co.jp/fcoding/articles/objc/08/objc08a.html
http://d.hatena.ne.jp/mswar/20090225/1235580859
http://developer.apple.com/jp/documentation/cocoa/Conceptual/ObjectiveC/4objc_runtime_overview/chapter_8_section_2.html

このへんが参考になりました。
あと、メモリリークなどを検出してくれる静的コード解析ツールの利用方法などはこちら

http://d.hatena.ne.jp/moto_maka/20090114/1231881156
http://www.j-love.info/natsu/iphone-app/natsus_note/dev/tips/iphonexcodebuild_and_analyze.html
http://ameblo.jp/micro-garden/entry-10462130384.html

今回とりあえずリンク列記しただけだけど、そのうちちゃんとまとめたいです。

UINavigationViewで、ひとつ上の階層のビュー(親ビュー)に戻るときにデータを渡す方法

http://kontonsoft.blog.shinobi.jp/Entry/50/

うーん、おっしゃるとおり。
データ保存用のクラスを作って、これを中継して親ビューにデータを渡していましたが、なんだかまどろっこしくてもっとすっきり変数を渡すいい方法はないのかと思っていたんですよね。だって、子ビューに移行するときには、生成した子ビューのviewControllerに変数を渡してからpushViewControllerすればいいだけでしょ?このくらい簡潔に変数の受け渡しができないもんかな、って思いますよね〜。

親ビューに戻る際には、popViewControllerというメソッドを使います。でも、親ビューは改めて生成するわけではないので、変数の受け渡しどころがなくって困ってしまいます。そこで見つけたのが上記サイト。ふむふむ、どうやらnavigationControllerには画面遷移の履歴を格納しているviewContollersというプロパティがあるようですな。

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    NSArray *array = self.navigationController.viewControllers;
    int arrayCount = [array count];
    HogeViewController *parent = [array objectAtIndex:arrayCount - 1];
    parent.piyo = piyo;
}

こいつを子ビューに実装。今回はこんな感じで解決。

ちなみに参考サイトでは、viewControllersのカウントから-2していました。これは、子ビューの任意のメソッドからviewControllersを呼び出したときには、子ビュー自身がarrayに含まれているので、親ビューは-2に相当しますが、viewWillDisappearから呼び出した場合には、すでにarrayから子ビュー自身が削除されているので、親ビューの位置は-1(=lastObject)に相当します。

インスタンス変数の生成と解放

http://blog.livedoor.jp/faulist/archives/1051536.html
http://ameblo.jp/xcc/entry-10400027873.html

インスタンス変数としてヘッダで宣言した変数は、deallocメソッドにてreleaseするのがメモリ管理の基本です。でも、後にreleaseするということは、allocで生成しておかないといけないんですね…。

message sent to deallocated instance

というエラーメッセージがでて落ちまくりました。ファクトリメソッドで生成したものはautoreleaseされてしまうので、deallocがコールされたときにはもうそんな変数ないよ、ってことですよね。今まで意識しないでやってきたけど全く気づかなかったな〜。

NSStringのインスタンス変数を使ったアプリが落ちまくったことで気づきました。参考サイトの中で「autoreleaseされたオブジェクトにreleaseを送ってはいけない。」という鉄則が書いてありました!なるほど〜、そのせいですね。deallocメソッドの中でreleaseしていたのをやめたことでめでたく解決、としようと思ったのですが、これだとメソッドを抜けた後に再び別のメソッドで使おうとすると解放済みになっている可能性があるので、retainしておく必要があるんですか。

string = [NSString stringWithString:@"hoge"]; //stringはインスタンス変数
[string retain];

としといて、deallocメソッドでreleaseすればよいわけです。これで無事解決!

ちなみにarrayという配列からNSStringを取り出して格納する場合、記述の方法でretainCountが変わるようです。

string = [array objectAtIndex:0]; //retainCount = 2
string = [NSString stringWithString:[array objectAtIndex:0]]; //retainCount = 1

UITableViewがスクロール時に落ちる件

なぜかはわかりませんが、UITableViewでplistより読み込んだNSMutableArrayを表示させると、スクロール時に落ちてしまう現象に悩まされました。

NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:@"data" ofType:@"plist"];
NSMutableArray *persist = [NSMutableArray arrayWithContentsOfFile:path];

こんな具合にplistより取得。すると、スクロール時にエラー。。
偶然発見したのが、3行めを、

NSMutableArray *persist = [[NSMutableArray alloc] initWithContentsOfFile:path]; 

に変更したらあら不思議、なぜか落ちなくなりました。なんで〜?

UINavigationViewで子ビューに移行するときの注意

子ビューからpopして親ビューに帰ってくると落ちまくる…。なんで〜。。
悩みあぐねいたあげく、原因発見。
テーブルのセルをクリックしたら子ビューに、というくだりの記述で、pushViewControllerしたあとに子ビューをreleaseしていました。がっくし。これからは生成時にautoreleaseするようにしておきます。


追記 9/15
pushViewControllerしたあとに子ビューをreleaseすると落ちる、ってかならずしも起こらないような〜。。