2008-06-24

[ヒレガス本]Chapter 4. Memory Management #2

前回より続き。

p.70
配列(NSMutableArray)にオブジェクトが追加されるときにオブジェクトはコピーされない。配列中に保持されるのはオブジェクトへのポインタ。で、そのとき追加されるオブジェクトに retain が送られる。配列が削除(deallocate)されるときに、格納されていた各オブジェクト(へのポインタ)に release が送られる。同様に、配列から要素(オブジェクト)が取り除かれるときにもオブジェクトに release が送られる。

Releaseに関する規則 (p.73)

  • alloc, new, copy および mutableCopy で作られたオブジェクトは retain count が 1 になっていて、autorelease pool には登録されていない。
  • 他のメソッドで作られたオブジェクトは、autorelease されている(retain count が 1)。deallocate されたくないときは retain する必要がある。

アクセサ

他のオブジェクトを保持するインスタンス変数があったとして、その setter をどう書くか。setter に今保持しているのと同じオブジェクトが渡される可能性に対応するため、以下のように書く必要がある。

- (void)setFoo:(NSCalendarDate *)x
{
    [x retain];
    [foo release];
    foo = x;
}

この方法以外にも、release と代入の前に(同じオブジェクトかどうかを)チェックする、あるいは release の代わりに autorelease を使う、というのもある。3つのどれでも良いけれど(それぞれにトレードオフがある)、この本では最初の「retain して release」を選ぶ、と。

これで終わり!!

Objective-C に関する説明はここまで。残りは Cocoa フレームワークに関する話題が中心になる、とのこと。(p.77)

2008-06-14

[ヒレガス本]Chapter 4. Memory Management

メモリ管理に関する章。Leopard 以降の特徴のひとつである garbage collection についての記述が始めに来る(p.66 - 68)。その部分を読んだところ。荻原(2.0)本(CHAPTER 06)では「ガーベジコレクション」となっているけど、ここでは Xcode の日本語表記に合わせて「ガベージコレクション」としておく。

ガベージコレクションについて覚えておくこと - その1

ビルドの設定を変える(p.67)。「必須 (-fobjc-gc-only)」を選べば良い。「サポートあり (-fobjc-gc)」の方は両モードで動けるようなフレームワークだとか、プラグインを作るときに選ぶもの。

そうそう、ここでちょっと驚いたこと。ヒレガス本の p.67 には、ビルドの設定の中から GC に関する項目を探すために「情報パネル」の検索ボックスを使うように書かれている。で、それを日本語環境でやろうとすると検索語も日本語で入力しないと見つけられなかった。「Garbage」では見つからず「ガーベジ」でも見つからず(だから荻原本の記述はちょっとマズいだろ)、「ガベージ」でようやく見つかった。検索ボックスはそういうものなんだとわかれば当たり前だけどね。というか、この挙動の方が直観的か。驚く方がヒネクレているんだろう・・・orz

ヒレガスは GC に完全に頼り切る前に、一通りは従来の方式(リテインカウント方式)も知っておくべきだと言う。以下、その部分を引用。

In the future, you may decide, "This retain-count stuff is for the birds. I'm going to use the garbage collector for everything." I wouldn't blame you. For now, however, learn about the retain-count mechanism. When you study old code or anything that uses lower-level frameworks (such as CoreFoundation), you will want to understand how it works.

古いコードを調べる必要があるかもしれないし、低レベル層のフレームワーク(CoreFoundationなど)を使うことがあるかもしれない。そうなればきっと、従来の方式がどう働くのかを知りたくなるよ、と。

過渡期なんだろうけど、つまりいずれ GC を使わない Cocoa アプリはなくなって、Xcode のオプションもデフォルトで -fobjc-gc-only が付くようになる(むしろコンパイラのデフォルトが変わるかも)。それは言い換えると、プログラマが Leopard より前の OSX のことをサポートしないと決めたとき、なんだけど。「ユキちゃん(Snow Leopard)」が出る頃には Tiger はいなくなっているかね。

ガベージコレクションについて覚えておくこと - その2

オブジェクトへの参照を保持している変数には、そのオブジェクトを使わなくなった時点で、nil を代入する。従来の方式で release するようなものか。これをやらないと、たとえローカルな自動変数であってもそのスコープにいる間はオブジェクトが回収されない。

ガベージコレクションについて覚えておくこと - その3

その3はない。Chapter 4 の残りはリテインカウント方式の解説だ、と。

荻原(2.0)本から拾ってみる

p.125 から引用。

両用が可能なコードを記述するのは大変困難であり、Apple 社のドキュメントでもそのような記述は推奨されていません。どちらのメモリ管理方式を採用するか決めたら、その方式で完全に動作するようにプログラミングするべきです。

「両用」というのは従来のリテインカウント方式と GC 方式のどちらでも動くように作るという意味。

過渡期っていうのは悩ましいね。一部のパフォーマンスに対する要求が厳しいプログラムを除けば GC を使うべきだろう。ただし、その場合 Tiger 以前のユーザを切り捨てることになる。

追記@2008-06-24: 続き

2008-06-09

[ヒレガス本]Chapter 3. Objective-C #2

ヒレガス本、p.44あたりから。

p.45の 「"Inherits from" versus "Uses" or "Knows About"」は、むやみに継承を使うな、という主張。何でもかんでも継承を使いたくなるのはオブジェクト指向プログラミングを覚え始めたときにみんな通る道だよねえ。ともあれ、Objective-C のような動的な言語では継承を使う必要性はほとんどない、とのこと(NSObjectを継承するのは別として)。この本(ヒレガス本)の中で継承はたった2個所しか使ってないゾ、と書いてある。

p.51で LotteryEntry の実装に description メソッドを追加しているけれど、ヘッダの方には宣言を加えていない。これは - (NSString *)description が NSObject で宣言されているメソッドだから。ちなみに、この description メソッドはデバッガでオブジェクトを "print" した際に呼ばれるそうだ(ADC Home > Reference Library > References > Cocoa > Objective-C Language > NSObject Protocol Reference)。デフォルトではクラス名と id 値を表示するだけ(荻原(2.0)本、p.148より)とのことなので、クラスを作ったときは専用の description を書いておくべきだろうね。デバッグしやすいから。

p.56。Cocoa のクラスイニシャライザの中には初期化に失敗すると nil を返すものがある。スーパークラスのイニシャライザが nil を返す場合、nil チェックをして自分も nil を返すべき。

p.57。designated initializer のことは荻原(2.0)本のp.55-56に「指定イニシャライザ」として記述がある。ヒレスガ本のこのページの最後に書いてあるのは、「もし初期化の際に引数が必須となるクラスを作ったら、スーパークラスの指定イニシャライザをオーバーライドして例外をスローするようにすべし」というもの。正しい引数なしにイニシャライザが呼び出されてしまうのを防ぐため。

覚えたこと

@"some text" 記法は 7bit で ASCII な文字しか書いてはいけない。つまり、日本語なんかは書けない。荻原(2.0)本だと p.177 の「オブジェクト定数の文字列」に同様の記述がある。@"..." 記法で書かれた文字列は NSString オブジェクト定数 (a string object constant) になる。

デバッガでオブジェクトの内容を表示させるのは "po" (print-object) コマンド。「内容を表示」といっても description メソッドを呼ぶだけ。

メッセージ送信が働く仕組み

p.62と63にある記述("For the More Curious: How Does Messaging Work?")。荻原(2.0)本だと p.148からの「07-02 メッセージ送信の仕組み」にもう少し詳しく書いてある。

オブジェクト(インスタンス)へのメッセージ送信は、コンパイル時に C の関数呼び出しの形に書き換えられる。

[myOjbect addObject:yourObject];

が、

objc_msgSend(myObject, 12, yourObject);

になる。レシーバーが第1引数、第2引数はセレクタ、以後はセレクタの引数が並ぶ形。上の例でセレクタを 12 という整数値で表しているけれど、これは「例え」であって実際に SEL 型が int だというわけではない。著者は、ほんとうはchar *なんだけど、int と考えると便利だよ、と言っているだけ。荻原本だと、SEL 型に関しては以下のように記述されている:

セレクタが異なれば対応する SEL 型の値も異なり、同じセレクタには必ず同じ SEL 型の値が対応します。Objective-C のプログラマは、SEL 型が具体的にどんな値を持つかについて知っている必要はありません。詳細は処理系に依存します。

このあたりのことは「ダイナミックObjective-C」というネット上のコラムに、もう少し踏み込んだ解説がある。

2008-06-05

[ヒレガス本]Chapter 3. Objective-C

覚えたこと

nil にメッセージを送ると・・・

(p.40)
In Objective-C, it is okay to send a message to nil. The message is simply discarded, which eliminates the need for these sort of checks.

Objective-C では nil にメッセージを送っても良い。単にメッセージが捨てられるだけ。エラーも例外も発生しない。C/C++/Java でおなじみの null チェックは不要だということ。

荻原(2.0)本だとp.62-63にかけて、このあたりの解説がある。ただ nil チェックを書かないことに関しては↓が著者の主張:

ただし、プログラムは簡単になりますが、「nil だった場合に何もしない」という意図が分かりにくくなることは確かです。従って、誤りが混入する危険性も高くなります。

これはおっしゃるとおり。プログラマはいつだって自らの意図をどうやってコードに込めようかと腐心しているのだから、意図を表現できる機会を減らすべきじゃない。っていうか、いろいろパターンを思い浮かべたけど「安全に nil チェックを省略できる」場合が出てこない。さらに、何も起こらないってことは nil にメッセージを送っているってこともわからないわけで、ヒレガス本の言う(p.40)ように「何でメッセージが届かないんだあ!!!」状態に陥りそう。

NSArrayには nil を入れられない

(p.44)
You cannot put a nil in an NSArray.

もちろん、NSMutableArrayでも同じ。これはちょっと意外だった。だから書き留めておく。で、nil が入れられない代わりに用意されたのが NSNull というクラス。使い方は↓

[myArray addObject:[NSNull null]];

さて、今日は p.54ぐらいまでで、時間切れ。夜が短いよ。


追記@2008-06-11:続きはこちら

2008-06-04

[ヒレガス本]Chapter 2: Let's Get Started (さあ、始めよう) #2

前回の続き。

デリゲートって何

荻原(2.0)本」の CHAPTER 14 にデリゲートの説明がある。要は下働きのためのオブジェクトってこと。

デリゲート機構を持つオブジェクトは何か特定のメッセージを受けたときに、もしデリゲートオブジェクトがセットされていれば、そいつに問い合わせたり、処理を任せたりする。このとき元のオブジェクトはセットされているデリゲートオブジェクトが送りたいメッセージを受け取れるかどうかを respondsToSelector:などで調べる。受け取れるオブジェクトならメッセージを送る(つまり、問い合わせたり処理を任せたり)。

デリゲートになるオブジェクトは送られる可能性のあるメッセージすべてに対応する必要はない。対応したいメソッドだけを実装すれば良いことになっている。だから元のオブジェクトは事前に調べる必要があるわけだ。まあ、なんというかデリゲートになる方がエラそうなわけだ。やりたい仕事だけ受け付けます、と。

さて、ここで(前回の)非形式プロトコルが登場する。デリゲートになるオブジェクトは好きなメソッドだけ実装すれば良いという規約なので、通常のプロトコルではまずい。プロトコルを採用するというのはそこで宣言されたすべてのメソッドを実装するっていう意味だから。そのための "ugly" な方法(ヒレガス本より)が非形式プロトコルだったわけだ。Objective-C 2.0 以降はオプション付きプロトコルが導入されて、もう必要なくなった。

2008-06-02

Google Site、作ってみた

作ってみた↓。

以下は、Official Google Blog での告知へのリンク。使い方の動画も貼られている。それを見るとだいたいの感じがわかる。英語だけど操作の様子が映るから問題ない。

[ヒレガス本]Chapter 2: Let's Get Started (さあ、始めよう)

書かれているとおりに手順を追って、RandomeApp を作ってみた。この程度のことは以前にもやったことはあったけど、まあ、それでも実際に手を動かしてみると楽しいものだ。

覚えたこと

Interface Builder で GUI 部品とコントローラなんかのインスタンスを結び付けるときの Ctrl-Drag の方向。メッセージを送る方から送られる方へ線を引く。ボタンなら、ボタンを押したときにメッセージがコントローラに送られるのだから、ボタンからインスタンスへ。テキストフィールドなら、関連づけられたインスタンス変数が変わったときに表示されるテキストが変わるようにしたいのだから、コントローラからテキストフィールドへ。メッセージの方向だと覚えれば良い。

調べたこと

awakeFromNibが気になった。調べてみた。NSNibAwakingという非形式プロトコル(informal protocol)だった。

非形式プロトコルって何?

ヒレガス本(p.297-298)によれば、2.0より前のObjective-Cで、デリゲート・メソッドを宣言するための "ugly" な手段だったということ。

荻原(2.0)本だと、p.272-274あたりに「非形式プロトコル」の説明がある。その最後の部分を引用すると、

従来は上の例のように、メソッド群のすべてが必ずしも必要でない場合、非形式プロトコルで宣言されるのが普通でした。Objective-C 2.0ではオプション付きのプロトコルが使えるようになりましたので、今後はプロトコルの利用が増えてくるものと思われます。

オプション付きのプロトコルって何よ?

プロトコルは Java の interface。荻原本(2.0)によれば Java の方がマネしたとのことだけど、まあそれはどっちでも良い。いわゆる「振舞いによる類型」を実現するための仕組みだ。平たく言うと「インタフェースの共有」ね。

で、2.0 より前の Objective-C ではあるプロトコルを採用(adopt)するとなったら、そこで宣言されたメソッドをすべて実装する必要があった。この仕組みだと先に出てきたデリゲート・メソッドの宣言に使うには都合が悪かったようだ。で非形式プロトコルのような "ugly" なやり方を取っていた、と。

これを解消するためなのかどうかは不明だけど、2.0 からはプロトコルを宣言するときに @optional@required というコンパイラ指定子を使えるようになった。荻原本(2.0)のサンプルを抜き出しておく。

@protocol Alarm
- (void)setCurrentTime:(NSDate *)date;
- (BOOL)alarm;
- (void)setAlarm:(BOOL)flag;
@optional
- (BOOL)snooze;
- (void)setSnooze:(BOOL)flag;
@required
- (void)setTimerAtHour:(int)h minute:(int)m;
@end

プロトコルに適合しているかどうかを conformsToProtocol で実行時にチェックできるのだけど、@optional なメソッドに関してはこれではチェックできない。調べるにはメソッドごとに respondsToSelector: でチェックする。

じゃあ、次はデリゲートって何、ってことを調べようと思ったけど、時間切れ。今夜はここまで。