Objective-Cで頭を悩ませる

Filed under Objective-C
Tagged as , ,

今回のエントリは、ここ数週間、Objective-CでiPhoneアプリ向けにコードをバリバリ書いているのですが、言語的に微妙に使い勝手が悪く、違和感を感じていたのですが、その原因の一つが分かった、という話です。
違和感の原因は、オブジェクト管理(メモリ的な意味)の分かりにくさ、でした。
私はC/C++での開発経験はあるので、ポインタが分からない〜、とかそういう話ではありません。(かつて、そういう時代もありましたがw)

retainしてんの?してないの?

retain/releaseという参照カウンタの仕組みは良いとは思うのですが、それ(retain)が全てではない、というところに問題の根源があると思うのです。
Counterphoto © 2008 Marcin Wichary | more info (via: Wylio)
copyするかもしれないし、assignかもしれない、という可能性を考えていたら、標準ライブラリの情報不足がジワリジワリと効いてきて、コードを書いてるのか、パズルを解いているのか分からなくなってきます。
例を上げてみます。
可変長の配列クラス NSMutableArray に addObject というメソッドがあります。
- (void)addObject:(id)anObject;
で、このメソッドが与えたオブジェクトをどう管理してくれているのか、それを知るすべが見つからないのです。
典型的な使用例として、下記のコードを見てください。このコードで問題ない、と言い切れるにはどれくらいの知識が必要でしょうか。

1
2
3
4
5
NSMutableArray* array = [[NSMutableArray alloc] init]; // (1)
NSObject* obj = [[NSObject alloc] init]; // (2)
[array addObject:obj]; // (3)
[obj release]; // (4)
[array release]; // (5)

(3)で渡したオブジェクトはretain/assign/copyの3通りの処理の可能性があります。
また、(5)のrelease時に、そのオブジェクトをどう処理しているか(releaseするのか、しないのか)を考える必要があります。
(3)で引数に渡したオブジェクトをretain、もしくはcopyしていたら、まぁ、(5)内では、オブジェクトをreleaseしてくれているとは想定できます。それをしないとほぼ確実にメモリリークしますしね。
しかし、(3)の引数処理がassign(参照渡しのみ)だったら、どうでしょうか?配列は破棄処理をするべきでしょうか。外部の使用者から見たら全く想像がつきませんね。
リファレンスにも次のようなことしか書いていませんでした。

addObject:
Inserts a given object at the end of the array.
– (void)addObject:(id)anObject
Parameters
anObject
The object to add to the end of the array’s content. This value must not be nil.
Important: Raises an NSInvalidArgumentException if anObject is nil.

anObjectはnilじゃだめだよ。nil渡したら例外投げるよ、ってことしか書かれていません。うーん、そうじゃなくて、引数を内部ではどう処理するのか、とかそういう情報も欲しいと思うんです。
結局下記のコードで、retainしていて、上記のコードで問題ないことを確認しました。

1
2
3
4
5
6
7
8
9
NSMutableArray *array = [[NSMutableArray alloc] init];
NSObject *obj = [[NSObject alloc] init];
NSLog(@" check A [%d]", (int)[obj retainCount]);	// 1
[array addObject:obj];
NSLog(@" check B [%d]", (int)[obj retainCount]);	// 2
[obj release];
NSLog(@" check C [%d]", (int)[obj retainCount]);	// 1
[array release];
NSLog(@" check D [%d]", (int)[obj retainCount]); // objがnilなので落ちる.

Open your mind!

retainも便利だとは思うし、積極的に使うべきだとは思うけど、明示して欲しいのですよ。
例えば、プロパティみたいに次のような構文が書けて、ヘッダーのメソッド宣言で分かればいいかも。
- (void)addObject:(id,retain)anObject;
あー、これだったらすっきりする!と自画自賛!!
特に標準じゃないライブラリとか、自分で書いたものとかですら、あとから使うときに困ることが多いんじゃないかな、と思うのですが、私だけでしょうか。


[AD]
↓「Chapter 3 オブジェクトの基本操作」が気になる

XcodeによるObjective-C入門

著者/訳者:大津 真

出版社:毎日コミュニケーションズ( 2010-01-23 )

定価:¥ 3,240

単行本(ソフトカバー) ( 304 ページ )

ISBN-10 : 4839931879

ISBN-13 : 9784839931872



↓この本で言語仕様をしっかり勉強すべきかも。「CAPTER13:オブジェクトのコピーと保存」が上記エントリと関係ありそうですね。

詳解 Objective-C 2.0

著者/訳者:荻原 剛志

出版社:ソフトバンククリエイティブ( 2008-05-28 )

定価:¥ 4,536

大型本 ( 560 ページ )

ISBN-10 : 4797346809

ISBN-13 : 9784797346800


3 Comments

  1. 通りすがり より:

    Objective Cのメモリ管理の大前提は、「オブジェクトが必要な時は自分でretainしなさい」ということです(メモリ管理のドキュメントに書いてあります)。

    これは、標準ライブラリにも当てはまります。例えばNSMutableArrayですが、addObjectされたオブジェクトはNSMutableArrayクラスで使われるので、NSMutableArrayがretainします。その後不要になったとき(removeObjectのとき)も、NSMutableArrayクラスがreleaseします。

    逆に言えば、「他のクラスがオブジェクトをretain/releaseするかしないかは気にするな」ということです。彼らは必要であれば勝手にretain/releaseするので、自分のコードの中でretain/releaseが合っていれば問題ない、と考えればよいでしょう。

  2. yamada より:

    >通りすがりさん
    コメントありがとうございます!
    そうですね。retain前提でやればいいというのは分かって来ました。

    しかし、それでも、内部の実装が分からないライブラリとかではassignかもしれない、という疑惑が1%でもあるかぎり、それを外側に明示する必要があるんじゃないかな、と思うのです。
    そしてそういう問題は言語仕様でなんとか回避できるようにしておくべきだったんじゃないかと思うんですよね。
    obj-cは、ユーザーのマナーにあぐらをかいているとでもいいましょうか。。

    iOS5以降のGCは未調査ですが、それでメモリ管理が楽になるなら嬉しいですね。

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)