Hatena::Groupiphone-dev

Ni chicha, ni limona - 平均から抜けられない僕

2009-07-06

[Cocoa] リファレンスカウンタ方式でのオブジェクトの代入について 19:09  [Cocoa] リファレンスカウンタ方式でのオブジェクトの代入について - Ni chicha, ni limona - 平均から抜けられない僕 を含むブックマーク はてなブックマーク -  [Cocoa] リファレンスカウンタ方式でのオブジェクトの代入について - Ni chicha, ni limona - 平均から抜けられない僕  [Cocoa] リファレンスカウンタ方式でのオブジェクトの代入について - Ni chicha, ni limona - 平均から抜けられない僕 のブックマークコメント

もう書くまでもないことですし、日本のiPhone開発本を開けば必ず載っていることなんですが、CocoaCocoa touchはメモリ管理形式にリファレンスカウンタ形式を用いています。


このリファレンスカウンタと、プロパティとの振る舞いでちょっと不安になったので、調べてみました。

試したのは以下のコードです。


@interface Hoge:NSObject {
	NSString *name;
}
@property (nonatmic, retain) NSString *name; // retain方式で宣言
@end

@implementation Hoge
@synthesize name;
	      :
	NSString *fuga = [[NSString alloc] initWithFormat:@"fuga%d", 1];
	NSLog(@"retainCount:%u", [fuga retainCount]);
	self.name = fuga;
	NSLog(@"retainCount:%u", [fuga retainCount]);
	self.name = @"Boke";  // ← 疑問:ここでちゃんとfugaをリリースしてくれるか
	NSLog(@"retainCount:%u", [fuga retainCount]);
	      :
@end

気になったのは、代入時にretainしてくれるのはともかくとして、別の値を代入した時(上記ソースのコメントがある箇所です)にきちんとreleaseしてくれるかどうか、という点です。


「ひょっとしてreleaseしてくれないのでは?」と思いましたが、なんのことはない、ビルド&Runしてみると、

XXXX[XXXX:XX] retainCount:1
XXXX[XXXX:XX] retainCount:2
XXXX[XXXX:XX] retainCount:1

上記の実行結果のとおり元々セットされていたオブジェクトもきちんとreleaseされていました。まったく問題なく、杞憂だったわけです。


よかったよかった*1


■assignの場合

・・・いやいや、上記だけだと誰にとっても限りなく意味のない情報になってしまいますので、プロパティを「assign」にしたときの結果も載せておきます。

XXXX[XXXX:XX] retainCount:1
XXXX[XXXX:XX] retainCount:1
XXXX[XXXX:XX] retainCount:1

このとおり、retainCountは変化しませんでした。最初にinitWith...で作ったときの値1のままで、変化していないことが分かります。

つまりオブジェクトへのassignプロパティは、「対象クラスにこのオブジェクトの所有権を持たせない」という意味にもとれますね。


■もう1つついでに

最後にもう1つ。


プロパティ宣言子は確かに便利ですが、古いバージョンのMac OS X(=Objective-C 2.0ではない)にも対応させたい場合には使用出来ません。

では、当時はどうやっていたかを参考として記述しておきます。これらはいずれもヒレガス本(Amazonインスタントストア)に書かれている方法です。

// 方法その1 : retainしてrelease
- (void)setFoo:(NSString *)x {
	[x retain];
	[foo release];
	foo = x;
}

------------------
// その2: 違うオブジェクトのときだけrelease&retain
- (void)setFoo:(NSString *)x {
	if (foo != x) {
		[foo release];
		foo = [x retain];
	}
}

------------------
// その3 : autoreleasePoolにつっこんでおく
- (void)setFoo:(NSString *)x {
	[foo autorelease];
	foo = [x retain];
}

それぞれのメリット・デメリットを以下に記しておきます。

方法メリットデメリット
その1ベーシックな方法。これでやっておけば取りあえず問題無し。毎回retain&release処理が入ってしまう。
その2同じ値のときはretainやreleaseのコストを避けられる毎回比較処理をおこなう必要有り。それに「同じ」とどう判断する?
その3あまり考えずに書ける。パフォーマンスの悪化。あと、取り扱いのミスが即クラッシュに繋がる。

こんなところです。

※最後にチラリと書きましたがautoreleaseのパフォーマンス悪化についてはiPhone開発では注意が必要です。そのうちちゃんと測って記事にしようと思っていますが、autoreleaseを使うよりもinit&releaseの方が処理が速いみたいですよ。


自分の場合、楽な構文やライブラリができると、その裏側がどうなっていたかをついつい忘れがちになります。今回のように、気になったときはきちんと確認しないといけないな、と自戒の念を新たにするのでした。


※hiroaki310さん、Twitterで的確なコメントをしていただきありがとうございました。

*1:最初はinitWithString:@"..."で作成したのですが、これだとretainカウントが-1、ではなくて2147483647(id:tokoromさん、ありがとうございました)になっていて計測できませんでした。これってクラスクラスタですよね?

tokoromtokorom2009/07/06 23:30retainCountの件、便乗して記事書かせていただきました。
http://iphone-dev.g.hatena.ne.jp/tokorom/20090706/1246890179

iPhoneの実行環境では、initWithString:@"..."のときのretainCountは-1ではなく2147483647になってます。
解放不要なオブジェクトはretainCountとしてNSUIntegerのMAX値(iPhoneではNSIntegerのMAX値)を返すようになってるみたいです。

horigoodhorigood2009/07/07 11:09>「同じ」とどう判断?の件
同一のオブジェクトかどうかは"=="で判定できます。内容の比較は、NSStringなら"isEqualToString:"、その他のクラスでは"isEqual:"で比較することになります。
ただし"isEqual:"の比較内容はクラスによって違うので注意が必要…と、たしか荻原本にありました。
けど、この比較の際にまたコストがかかるようだと、あまり意味ないですね。w

paellapaella2009/07/07 12:12tokoromさん、ありがとうございます。私も最初はinitWithString:@"..."を試したのですが、このときのretainCountは-1ではなく2147483647になってしまって、減っているかどうかが分からずに困ってしまいました。

NSStringの実装の裏側では、状況によって色々なクラスが暗躍しているのが垣間見えて、なかなか面白かったです。

あと、私よりも参考になる記事を書いていただいてありがとうございます。日本のiPhoneアプリ開発が活発化すると良いですね。

horigoodさん、仰るとおりです。
ちょっと私の説明が不足していたのですが、オブジェクトがNSString以外の、比較にコストのかかるオブジェクトだとすると、どれを選ぶかで検討が必要だと思います。

Appleのデベロッパーも「Macとは違うから。二度言うけれど違うから」と言っていますので、やっぱり意識しないといけないですね。

tokoromtokorom2009/07/08 16:33コメントで一部間違ったことを言ってました。
retainCountが「-1」になったり「2147483647」になったりするのはiPhoneどうこうって話じゃないですね。「2147483647」しか見たことがなかったのですみません・・・

●まとめ
オブジェクト定数: 2147483647 == INT_MAX == NSIntegerMax
解放できないオブジェクト: -1 == UINT_MAX == NSUIntegerMax

paellapaella2009/07/09 00:59ご指摘ありがとうございました。修正しておきました。

恐縮です。

tokoromtokorom2009/07/09 01:48私もきちんと整理できていなかったところなので勉強になりましたー^^
ありがとうございました。

ErikaErika2012/08/08 21:29This piece was a lifejacket that saved me from drwoinng.

itlfgwvcwiitlfgwvcwi2012/08/10 03:52Y7Tfuq <a href="http://yrviiqigchdp.com/">yrviiqigchdp</a>

cpiehilohcpiehiloh2012/08/12 10:03P110j0 , [url=http://astocxyplnjv.com/]astocxyplnjv[/url], [link=http://gdvgbpaaxrrn.com/]gdvgbpaaxrrn[/link], http://smmosyueefjc.com/