Posted by yamada on 2010/11/07 – 03:11
今回のエントリは、ここ数週間、Objective-CでiPhoneアプリ向けにコードをバリバリ書いているのですが、言語的に微妙に使い勝手が悪く、違和感を感じていたのですが、その原因の一つが分かった、という話です。
違和感の原因は、オブジェクト管理(メモリ的な意味)の分かりにくさ、でした。
私はC/C++での開発経験はあるので、ポインタが分からない〜、とかそういう話ではありません。(かつて、そういう時代もありましたがw)
retainしてんの?してないの?
retain/releaseという参照カウンタの仕組みは良いとは思うのですが、それ(retain)が全てではない、というところに問題の根源があると思うのです。
photo © 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,150
Amazon価格:¥ 3,150
単行本(ソフトカバー) ( 304 ページ )
ISBN-10 : 4839931879
ISBN-13 : 9784839931872
↓この本で言語仕様をしっかり勉強すべきかも。「CAPTER13:オブジェクトのコピーと保存」が上記エントリと関係ありそうですね。

詳解 Objective-C 2.0
著者/訳者:荻原 剛志
出版社:ソフトバンククリエイティブ( 2008-05-28 )
定価:¥ 4,410
大型本 ( 560 ページ )
ISBN-10 : 4797346809
ISBN-13 : 9784797346800
Posted by yamada on 2010/10/24 – 10:00
Objective-Cをなんとなく使い続けていたのですが、分かってるようであまり分かっていなかったのがプロパティ。
retain/release/autorelease周りとの絡みもあってなんとなくやり過ごしていたのですが、ちょっと切羽詰ってきたので仕切りなおして勉強してみました。
NSStringを持つクラスでテスト
いろいろ調べた結果、とりあえず、下記のようなクラスStringContainerを定義しておけば問題なく使えるっぽいです。(問題見つけたら教えてください!)
まずは、定義ファイル。内部にNSString*のインスタンス変数を二つと、そのプロパティ宣言を行っています。
重要なポリシーは、propertyは内部でコピーする、ということ。下記のページを参考にしています。
1
2
3
4
5
6
7
8
9
10
11
12
13
| // StringContainer.h
#import <Foundation/Foundation.h>
@interface StringContainer : NSObject {
NSString* str0;
NSString* str1;
}
// コピーするので、渡すものはautoreleaseしておいてください.
@property (nonatomic,copy) NSString* str0;
@property (nonatomic,copy) NSString* str1;
@end |
次に実装コードです。プロパティ設定のためのsynthesizeと、デストラクタを定義しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // StringContainer.m
#import "StringContainer.h"
@implementation StringContainer
@synthesize str0, str1;
-(void) dealloc{
[super dealloc];
[str0 release];
[str1 release];
}
@end |
そして、テストコード。str0とstr1では別の与え方をしてみました。
1
2
3
4
5
6
7
8
| // Test.m
// 確保.
StringContainer* scTest;
scTest = [[StringContainer alloc] init];
// 各パラメータを設定.
scTest.str0 = [NSString stringWithFormat:@"Default 0[%d]",0];
scTest.str1 = [[[NSString alloc] initWithFormat:@"Changed 1[%d]",11] autorelease]; |
str0に渡す文字列は、stringWithFormatはクラスメソッドで生成。なので、そこで生成されたオブジェクトはそちらでAutoRelease Poolに入っているようなので、特にrelease/autoreleaseを行う必要はありません。むしろ、それをやると、retain countが0になったときに、二重にreleaseが呼ばれるので、問題となります。
str1に渡す文字列は、alloc, initで生成したもの。ここで生成しているので、ここでautoreleaseしています。代入後に即releaseしても問題有りませんが、ここでrelease/autoreleaseのどちらかを行っていないと、そのメモリは破棄されないので、リークしてしまいます。
これでOKのハズ!
一応、Instrumentsツールを使ってリークを調べたりもしたので、これで大丈夫だと思うのですが、認識に問題があったら誰か教えて欲しいです!!!
たったこれだけのことを自信持ってかけるようになるのに半日かかりました!
AutoRelease Poolというものの存在、クラスメソッドで定義したものがそちらで勝手にautoreleaseされているっぽい、ということ、それらをググって知ったので理解できたのですが、そうでなかったらなかなか辿り着けないのです。
ちなみに、NSArrayも同様な書き方でプロパティ実装できるはずです。
[AD]
↓この本、買いました!ウェブで連載されていたコラムの総集編とiPhone Hackコラムの書き下ろし。読み物として書かれているので、飽きずに読めてる!Objective-Cの実装部分まで踏み込んでいるので、ダイナミック言語としての面白さを知ることができるし、後半ではデザインパターンの実装の話にもなる。

Dynamic Objective-C
著者/訳者:木下 誠
出版社:ビー・エヌ・エヌ新社( 2009-03-27 )
定価:¥ 3,360
Amazon価格:¥ 3,360
単行本(ソフトカバー) ( 456 ページ )
ISBN-10 : 4861006414
ISBN-13 : 9784861006418
↓これも店頭で見ていい!と思って買いました。iPhoneプログラムのコネタ集なんだけど、一つ一つが過不足無く説明とコードが書かれていて、本当にすばらしい!版型もちょっと小さいので、鞄に入れて移動中に気になるところをちょっとずつ読むスタイルもオススメ。

iPhoneSDK開発のレシピ
著者/訳者:高山 恭介 広部 一弥 松浦 晃洋
出版社:秀和システム( 2010-03 )
定価:¥ 2,520
Amazon価格:¥ 2,520
単行本 ( 319 ページ )
ISBN-10 : 4798025798
ISBN-13 : 9784798025797
Posted by yamada on 2010/03/17 – 08:00
iPhoneアプリを開発していて、Objective-Cを普通に使っているのですが、慣れないコードを書こうとすると、作業が止まってしまうことが多い。
これは細かいところの言語仕様を完全に把握しているわけではないのが原因で、1日くらいiPhone SDK抜きでObjective-Cでいろいろクラスを書いてみて仕様をしっかり覚えればいい、というのは頭では分かっているのですが他の仕事でいろいろな言語を渡り歩いているのもあり、なかなか一度しっかりと勉強する時間が取れない。
で、そんな自分の為にすぐにコピペできるようにコードのメモです。
プロパティの書き方
というわけで、Objective-Cでのプロパティの書き方メモ。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // Hoge.h
@interface Hoge: NSObject{
// 以下のように内部変数を作っておく。
int testInt;
int testInt2;
NSString* testStr;
int coreValue;
}
// プロパティ名を宣言.
@property (readwrite) int testInt; // 一番簡単なプロパティ宣言.
@property (readonly) int testInt2; // 読込み専用プロパティ宣言.
@property (copy, readwrite) NSString* testStr;
// getter, setterを指定するプロパティ宣言(setter関数名に":"がついているのに注意).
@property (readwrite, getter=getSomeValue, setter=setSomeValue:) int someValue;
@end |
hogehoge.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| #import "Hoge.h"
@implementation Hoge
@synthesize testInt;
@synthesize testInt2;
@synthesize testStr;
// コンストラクタ.
- (id) init{
}
// getter, setter.
-(void)setSomeValue:(int)value{
coreValue = value;
}
-(int)getSomeValue{
return coreValue;
}
@end |
参考サイト
[AD]
↓Objective-Cを詳細に知るにはこの本がいいかもしれない!「なか見!検索」で見てみると、リファレンスカウンタについても書かれているみたいだし。

詳解 Objective-C 2.0
著者/訳者:荻原 剛志
出版社:ソフトバンククリエイティブ( 2008-05-28 )
定価:¥ 4,410
大型本 ( 560 ページ )
ISBN-10 : 4797346809
ISBN-13 : 9784797346800
↓iPhone用ACアダプタで、ケーブル巻き取り式な上に、USBコネクタがあるのでさらにもうひとつ何か充電できるスグレモノ!
↓iPhone用USBコネクタでケーブル巻き取り式。カバンに一つ入れておくと良さげ。