Objective-C再入門
すっかり忘れてしまっていたので詳解 Objective-C 2.0 改訂版を一通り読んだ。
この本はみんなに勧められるだけありますね。
「こういう問題点があります。だからこうします。」
の流れがわかりやすくて読んでて楽しかったです。
問題となるようなサンプルコードを上手く持ってくるのもすごいなぁと思いました。
ということで、読んだメモ。
インタフェースの概念
- 重要なのは「使いたいクラスでどういう呼び出しができて、どういう戻りがあるのか」ということ
- なのでObjective-Cではinterfaceという中でそれを書いている
- こういう「送信」をしてくれれば、こういう「応答」をしますよ
- これを.hに書いてくれているので、そのクラスを使う人は.hだけ見ればいい
- .mでどんな実装してるかなんて興味ない
なんでNSってのが頭に付いてるのが多いの?
- 元々NeXT社がObjective-CでNEXTSTEPというのを作っててそれの略語
- ついでにnibファイルは何の略?(p.377)
プライベートな名前としての接頭語_
- Appleが予約しているので接頭語にアンダースコアを付けてはいけない
ヘッダ中の引数や返り値の宣言で、別のクラスの型を使う場合の記述
- #importでそのクラスのインタフェース部分をimportしても良いが、ヘッダファイルは様々情報が書いてありコンパイル時の処理が重くなることがある
- 単純に「これはクラス名だよ」とコンパイルに教えたいだけなら@classで宣言してあげれば良い
- そのクラスのメソッドも使うのであれば、実装部分でヘッダファイルをimportしなければいけない
キャストを使うべき場所
メソッド内で自分自身のメソッドを呼び出す時などに固定のクラス名を書かない方が良い(p.309)
- サブクラスが継承していた場合、固定のクラス名を書いてしまうとそのクラスに依存してしまうが、selfにしておけばサブクラス自身を表すことになる
インスタンス変数とアクセサとプロパティ
- インスタンス変数はインタフェースの{}に書かれる変数のこと
- アクセサは->でアクセスしなくていいようにゲッタとセッタのメソッドを用意しておくこと。ちなみにコーディング規約でゲッタの接頭語にgetは付けない。
- プロパティは「外部からアクセスできるオブジェクトの属性」という意味
ドット演算子(p.298-)
メソッドとnil
- 値がnilの変数にメッセージを送っても「メッセージはどこにも送られない」(p.63)
@propertyのretainオプションとnonatomicオプション(p.295)
- retainオプションあり+nonatomicオプションなしの場合、セッタ内部でretain+autoreleaseが行われる
- retainオプションあり+nonatomicオプションありの場合、セッタ内部で「元の保持しているオブジェクトをrelease」+「新しく保持するオブジェクトをretain」が行われる
- つまり、プログラムの最後にreleaseする必要がある
- 全てのretain+autoreleaseプロパティの値をreleaseするためにreleaseのメッセージを最後にまとめて送っても良いが、nilを代入する方が良さそう
- nilにメッセージを送っても「メッセージはどこにも送られない」ため「nilにretain」が行われても問題はない
- この辺の話は「プロパティ解放の記述方法についての提案 - iPhoneアプリ開発まっしぐら★ - iPhoneアプリ開発グループ」「前記事の補足:プロパティへのnil代入がreleaseの代わりになる理由 - iPhoneアプリ開発まっしぐら★ - iPhoneアプリ開発グループ」を参考に
クラスメソッド
- クラスメソッド実行中のselfはそのクラスオブジェクトを表す
クラス変数
- static指定子はファイル内部でのみ有効という性質を利用し、static指定子をクラス変数としてる
- staticだと継承したときにサブクラスからスーパークラスにアクセスできない問題があるが、そもそもアクセサメソッドを用意すべきだし、用意すれば問題ない
動的にクラスを生成
- AとBというクラスがあったときに、動的にクラスを生成したい時はclassというクラスメソッドを使う
Class theClass = flag ? [A class] : [B class];
動的にメソッドを実行
- @selectorを使うとメソッド式の内部表現(SEL型)を変数に代入できる
- またその内部表現はperformSelectorでメッセージ送信できる
- それらを利用すると以下のように動的に実行できる
SEL method = (cond1) ? @selector(activate:) : @selector(hide:); id obj = (cond2) ? myDocument : defaultDocument; [target performSelector:method withObject:obj];
あるインスタンスオブジェクトがあるメソッドを実装しているかどうかの確認
- respondsToSelectorにSEL型にしたメッセージ式(@selectorで作ったもの)を渡してあげる。YESなら実装してある
あるインスタンスオブジェクトがあるクラスのインスタンスかどうかを確認
- isMembaerOfClassというインスタンスメソッドがあるのでそれを利用する
BOOL isMember = [someobj isMemberOfClass: [A class]];
メモリ管理
- オーナーシップ・ポリシーに従ってプログラミングしましょう
- alloc..., copy..., retain..は、保持したインスタンスオブジェクトが不要になった場合に解放する責任がある
- release, autoreleaseメッセージを送りオーナーシップを解放する
- alloc..., copy..., retain..は、保持したインスタンスオブジェクトが不要になった場合に解放する責任がある
- 引数や返り値でオブジェクトを渡す場合はオーナーシップの移動は起こらない
- メソッド内でtmpオブジェクトを作り、それオジェクトを返り値にする場合は、returnのタイミングでオーナーシップを放棄する
id tmp = [[ComplexData alloc initWithData:myValue];
return [tmp autorelease]
保持の循環
- 保持の循環を避けるため、親子関係なら親は子のオーナーシップを持ち、子はオーナーシップを伴わないようバックポインタというポインタの参照だけにする
配列を使う時の注意点
- インスタンスのオブジェクトを配列の要素にしたタイミングで、配列retainをオブジェクトに送りオーナーシップを持つ
- なので、オブジェクトを配列に入れるタイミングでautoreleaseをするか、入れた後にreleaseをして、後の管理は配列に任せる(p.204)
フレームワーク(framework)はソフトウェアを構成し、実行するために必要なクラスライブラリやヘッダファイルなどをまとめて一つにしたもの
- クラスライブラリは実行時に動的にローディングされるように構成されていて、アプリケーションによって共有される
ブロックオブジェクト(p.322-)
- クロージャのこと(C言語の拡張)
- C言語の場合「function」や「sub」の用に関数を表すキーワードがないため^を使ってると思えば良さそう
- 自動変数はブロックリテラルが記述されたタイミングで保持され、ブロックオブジェクト内では参照しか行えない
- グローバル変数やstatic変数は変更可
- Objective-CでコンパイルしたブロックオブジェクトはObjective-Cのオブジェクトとして振る舞う
- Objective-Cで扱う場合ブロックオブジェクトをcopyメソッドで複製できるが、その時のリファレンスカウンタの動きには注意
アプリケーション固有の初期設定はmainではなくUIApplicationのデリゲートオブジェクト内に記述(p.391)
- アプリケーションの起動直後、起動が終わったことを知らせるメッセージがデリゲートに送られるのでその内部に記述する
- アプリケーションが切り替わったとき、終了する直前に行いたい動作も
アサーション(p.447)
GCD(p.472-)
- それぞれの作業をブロックオブジェクトとして記述しキューに入れるとシステムが最適なスケジューリングで処理してくれる
キー値コーディング key-value coding,KVC(p.514-)
- アクセサメソッドが実装されてなく、宣言プロパティがなくてもプロパティにアクセスできる
- 強力な機能なのでその分注意が必要
- setValue:forKeyを使って値をセット、valueForKeyを使って値をゲットする
- 受け渡しは全てid型になるのでプロパティに間違った値を渡さないように注意
- 全てid型でのやり取りになるため、スカラ値などの「オブジェクトでない値」の場合は「セットする時にはラップする」「ゲットはラップしたオブジェクトで戻ってくる」という点に注意