インスタンス変数へのポインタの解放

インスタンス変数をポインタへ格納したら、そのポインタって解放しちゃいけなんですね〜。ポインタ解放後にインスタンス変数へアクセスすると落ちるというのに気づかず、今回もどっぷりハマりました。。
ていうか要するにポインタ、ってまだぜんぜんよくわかってないんだよね。

そしてややこしいのが、インスタンス変数に格納したポインタは、格納後にリリースしてももちろん問題ないんですね。。これは普通にやるもんね。


はぁ〜、C言語、勉強しないと。。


追記 9/15
これってC言語、というより、ObjectiveC特有のretainCountによる問題ですよね。「インスタンス変数に格納したポインタは、格納後にリリースしてももちろん問題ない」と書きましたが正確でなかったです。インスタンス変数への代入も、そのままhoge = piyo;と代入するのと、self.hoge = piyo;と、アクセサメソッドを利用して代入するのとではretainCountが変わってきます。

hogeインスタンス変数、piyoのretainCountが1の場合、

hoge = piyo; //retainCount=1
self.hoge = piyo; //retainCount=2

なので、1行目の記述でhogeへpiyoを代入した場合、代入後にpiyoをreleaseしてしまうと、retainCountが0になりdeallocされます。つまり、その後hogeへのアクセスが生じるとエラーが起こるわけです。

アクセサの定義時に(nonatmic, retain)と機械的に書いていましたが、このretainはセッターメソッド経由時にretainしますよってことなんですね〜。

アプリ起動時に表示されるスプラッシュスクリーンに関して

http://stackoverflow.com/questions/3042896/cant-remove-default-png
http://www.iphonedevsdk.com/forum/iphone-sdk-development/39435-remove-splash-screen.html

ProjectのResourcesフォルダにdefault.pngというファイル名で画像を追加すると、アプリ起動時にスプラッシュスクリーンを表示できるというのはよく知られています。でも気が変わってやっぱりスプラッシュスクリーンの追加をやめたい場合どうすればいいのでしょう?だって一度でもスプラッシュスクリーン追加後にビルドするとResourcesフォルダ内のdefault.pngを削除してもゾンビのごとく表示されてしまうんですよ。上記サイトでは、
1:iPhone Simulatorにインストールされたアプリを一度削除してから再ビルドする方法
2:BuildメニューのClean All Targetsを行なってからビルドする方法
の二つが紹介されていましたが、どちらをやってもダメ〜。
ネットを探しまくってもこれ以外に決め手になる方法は見つからないし。。ふてくされつつひらめいた最終の方法。コードに何かしら変更を加えてから再度ビルド。するとどうでしょう!見事スプラッシュスクリーンが削除されています!!
おそらく上記1、2のどちらか(どっちも?)が効いていたのでしょう。その上でコードに変更点がないとちゃんと再ビルドしてくれないようですね。いや〜、今日もどっぷりハマってしまった。。

Interface Builderを使わずにUIViewを記述する方法

参考:「iPhoneプログラミング UIKit詳解リファレンス」 リックテレコム P.21-27
http://token.sakura.ne.jp/wp/

上記参考書籍、大変参考になりますが、nibファイルをすべて削除したときのUIViewControllerのviewの取り扱いについては詳しく書かれてませんでした。AppDelegate内で、windowに直接viewを追加する方法しか書いてないんですね。。

IBを使った場合は、MainWindow.xib内で指定したUIViewControllerに自動でviewが追加されますね。なので、nibを削除してしまった場合は、AppDelegate内でUIVIewControllerを記述してやらないといけないんですね。View-based Applicationを選択してHogeという名でプロジェクトを作成した場合を例に手順を追って説明します。


手順1:nibファイル(MainWindow.xibとHogeViewController.xib)を消去
まずは不要なnibをすべて消去します。


手順2:Hoge-Info.plistを編集
Hoge-Info.plistを開いて、Main nib file base nameと書いてある行を削除。この行で指定しているのは、maim.mが実行されたときに読み込まれるnibの名前なのでここで削除してしまいます。


手順2:main.mのコードを編集

    int retVal = UIApplicationMain(argc, argv, nil, nil);

通常4つめの引数はこのようにnilで、この引数はデリゲートの名称を指定するためのものです。nibがある場合はデリゲートの指定はnibが行なうのでnilとなってますが、nibを削除しているのでデリゲートの名称を明示してやらなくてはなりません。ということでここにデリゲートの名称を記入します。

    int retVal = UIApplicationMain(argc, argv, nil, @"HogeAppDelegate");


手順4:HogeAppDelegate.mにコードを追加

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
	window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; //nibの代わりにwindowを作成
	viewController = [[HogeViewController alloc] init]; //nibの代わりにviewControllerを作成
	
    // Add the view controller's view to the window and display.
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];

    return YES;
}

// Override point〜以下2行を追加してます。
つまり、ここでnibに代わってwindowとviewControllerを作成してるんです。でもこれだけじゃだめなんですね。


手順5:HogeViewController.mにコードを追加

// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
	CGRect screenBounds = [[UIScreen mainScreen] bounds];
	UIView *rootView = [[UIView alloc] initWithFrame:screenBounds];
	rootView.backgroundColor = [UIColor redColor]; //わかりやすいように赤背景にする
	self.view = rootView;
	[rootView release];
}

コメントアウトされているloadViewメソッドの中にコードを追加。コメント行に英文で記述してあるように、コードで直接viewを記述するのがこのメソッドの役目のようです。このメソッド、参考サイトの記述によると、nibからviewがロードできなかった場合に読み込まれるようですね。nibは上記コードの代わりに自動でviewを作成してくれていたのです。今回は参考までに赤い背景色のviewを追加しただけですが、実用的なviewを作成するためには、別にUIViewのサブクラスを作成して、それをここで読み込んでやるのがいいでしょう。

あと、IBOutletの指定が必要なくなったので、HogeAppDelegate.hの@propety以下と、HogeAppDelegate.mの@synthesize以下のアクセサメソッド定義部分は必要なくなります。ついでにこいつも消去しちゃいましょう〜。

UILabelをcenterプロパティで座標指定すると、文字がボヤッとしちまうことが多々ある件

http://d.hatena.ne.jp/KishikawaKatsumi/20100527/1274910461

はて、なぜだろう?
UILabelの座標指定に、小数を使うとボケっとしてしまうことがあるのは理解できますが、

label.center = CGPointMake(100, 100);

などと、整数で指定してもぼけてしまうことがありまする。
これって、UIlabelのsizeが奇数だった場合に起こる問題のようでして、つまりcenterで指定した座標がいくら整数でも、sizeが奇数の場合、自動生成されたframeの値が小数で指定されてしまう、ということに起因しているようです。

にゃるほど。で、対策は、CGRectIntegral (CGRect rect)で、CGRectの値を丸めてしまう、という方法があるそうですな。

label.frame = CGRectIntegral (label.frame);

などとすれば晴れてビシっと表示されるはず。
ちなみにこの現象はUIviewのサブクラスではみな同じように起こる現象のようです。

addSubview:でUIViewに追加したsubviewのうち、一番上の階層のviewを取得する方法

int last = [self.subviews count] - 1;
UIView *hoge = [self.subviews objectAtIndex:last];

今回は参考サイトが見つかんなかったのでめずらしく自力で。
一発でサクッと取得できるメソッドはないようなので、まわりくどくsubviewの数を取得。そしてその数から-1したものが最後に追加したviewのindexとなるのでこれを使って取得完了〜。

UIAlertViewのmessageを左寄せにする方法

http://www.iphonedevsdk.com/forum/iphone-sdk-development/11676-left-align-text-uialertview.html

今回も助っ人ガイジンさん大活躍。

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"hoge" 
	message:@"hogehoge"
	delegate:self 
	cancelButtonTitle:@"OK" 
	otherButtonTitles:nil];

[alert show];
[alert release];

((UILabel *)[[alert subviews] objectAtIndex:1]).textAlignment = UITextAlignmentLeft;

てな感じ。
通常のアラート作成の後に最終行を追加するだけ!

つまりUIAlertViewのmessageって、UILabelとしてUIAlertViewの subviewとして生成されてるんですね〜。
それを知ってる人はこうしてプロパティを設定できるわけですな。こういうスマートな解決策大歓迎。考えたガイジンさんは超Cool!

文字列比較のお作法

参考:「入門 Objective-C 2.0」 翔泳社 P.139

いや〜、はまった…。

文字列の比較。ただ単に==演算子で比較してもダメなんですね…。つまり、

NSString *hoge1 = @"piyo";
NSString *hoge2 = @"piyo";

if (hoge1 == hoge2) {
	//
} else {
	//何を試してもこっちに判定。。
}

ではダメ。いくら同じ文字列を比較してるつもりでもはじかれてしまう。ようやくたどり着いた解答がこちら。

NSString *hoge1 = @"piyo";
NSString *hoge2 = @"piyo";

if ([hoge1 isEqualToString:hoge2]) {
	//ようやくこっちに判定!!
} else {
	//
}

が正解。理由は==演算子ではポインタ値の比較となるからとのことですが、もちろん通常のオブジェクトであればこれで問題ありません。ただ、文字列に限ってはダメなんだそうで。ああ、そうだったの〜。だ~って、==演算子でも問題なかったこともあったから発見が遅れたんじゃないの〜。ブツブツ。

あとこういう表現もあるんだって。これ使えそうだな。

if ([hoge hasPrefix:@"-"]) //最初に含まれるかどうか
if ([hoge hasSuffix:@".jpg"]) //最後に含まれるかどうか