CHAPTER13 オブジェクトのコピーと保存
2011/10/15(土) 22:44 Objective-Cはてブ情報 はてブに登録 はてブ数

浅いコピーと深いコピー

オブジェクトのポインタをコピーするshallow copyと、新たなメモリ領域を持つポインタを作る深いコピーdeep copyがある。

ゾーンについて

copyWithZoneメソッドというようにゾーンは今も名残が残っている。
動的なメモリ確保のヒープ領域はアドレス空間では幅広く使われたりしますが、空間的な局所参照性や仮想空間などアクセスが集中する系のデータの場合はある程度近い場所に配置したほうが効率が良くなります。
このヒープ領域をある程度区切って、関係の深いデータやオブジェクトはその区間=ゾーンを利用できるようにしていました。
現在はデフォルトゾーンでも十分効率化されているため殆ど明示的に使うことはなくなりました。^

コピーメソッドの定義

Objective-CのNSObjectにはcopyメソッドがあり、新しいインスタンスを作成できます。
しかし、copyメソッドの実体はcopyWithZoneというインスタンスメソッドで、デフォルトゾーンに新たにインスタンスを作成します。
copyWithZoneはNSCopyingプロトコルで実装されてる。

オブジェクトのアーカイブ化

属性値や相互関係も含めてオブジェクトをバイト列に変換されたものをアーカイブという。
複合手段はアンアーカイブといい、これはNSCoderのサブクラスで定義されている。

プロパティリスト

ASCII、XML,バイナリの3形式がある。
プロパティリスト全体をNSDictionaryのインタンスに格納した時、これをルート辞書とよび、この辞書のキーは文字列でないと行けない。
ASCIIはOPENSTEPからの互換性のために存在し、このplistデータからオブジェクト構造を複合するには
propertyListメソッドを使用します。
バイナリデータ<16>と(配列)、辞書を合わせた形。

XML形式のプロパティリスト

writeToFile/ContentsOfFileで読み書きができる。

CHAPTER12 宣言プロパティとアクセサ
2011/10/10(月) 21:29 Objective-Cはてブ情報 はてブに登録 はてブ数

プロパティは概念的に
  • 宣言プロパティ
  • アクセッサによるプロパティ
  • key valueコーディングにおけるプロパティ
の3つがあり、上ほど狭い概念という感じ。

プロパティに関するイントロスペクション

クラスにどんな宣言プロパティが備わっているか、そのシグネチャについて動的に調べることができる。
あるクラスが持っているメソッドやプロパティの型や名前といったメタ情報へアクセスする機能の事をイントロスペクションorリフレクションという。
Objective-CではresponsedToSelector:メソッドなどもイントロスペクションに該当する。

プロパティの実装

@propertyで宣言したものは@synchronizeでアクセサを実装できる。
@synchronizeの位置は@implementationから@endまでの間ならどこでもいい。
@synchronizeを使わないで手動でアクセサを書く場合は@dynamicというコンパイラディレクティブを使う。

@synchronizeとインスタンス変数

@synthesize item; としたときにitemのインスタンス変数が無い場合、"脆弱ではないインスタンス変数"を持つモダンランタイムは自動的にインスタンス変数を@propertyで宣言された型で作成するようになっている。できるだけインスタンス変数はちゃんと作るようにしておく。

@propertyと@synchronizeどちらもカンマ区切りで複数の宣言、実装を一行で書ける。

プロパティの属性指定

プロパティのgetter,setterは次のようにメソッド名の指定などもできる。
@property(setter=setValue:) int hitPoint;

アトミック性

nonatomicというのはatomicではないという意味で。アトミックとは並列プログラミングの概念。
nonatomicはそれぞれのgetter,setterが複数のスレッドで同時に実行されるかもしれないメソッド定義のことで、atomicはその逆。つまりnonatomicではない場合、lock,unlockに囲まれたような動作になる。

ドット演算子

Objective-C2.0ではプロパティへのアクセスにドット演算子を使えて、コンパイル時にアクセサの呼び出してとして解釈される。クラスを型として設定した変数に限定されるので、id型の変数にはドット演算子を使えない。

構造体のメンバ参照との混用

プロパティを参照するためのドット演算子と構造体へのメンバ参照のドット演算子は混用しがち。
例えばNSSize型は構造体なので、
w = win.minSize.widht;
// 以下と同じ
w = [win minSize].width;
// 構造体のメンバ参照であるので、次のように代入はできない
win.minSize.width = 320.0;
// 意図することを書くには次のようにする
NSSize sz = win.minSize;
sz.width = 320.0;
win.minSize = sz;// sizeをまるごと変えることはできる

CHAPTER11 プロトコル
2011/10/09(日) 23:41 Objective-Cはてブ情報 はてブに登録 はてブ数

プロトコル

継承を用いた場合、スーパークラスの実装がサブクラスに影響するが
プロトコルの場合は宣言だけの集合のため実装は各プロトコルが適応されたクラスによる。

プロトコルは複数のプロトコルを適応することができる。
@interface A: NSObjct <S, T>
//
@end
だが、この時、プロトコル間で返り値が異なるような同じ名前のメソッドが重複していている場合は適応することができない。

プロトコルを指定した型宣言

id <S> obj;
のように変数がプロトコルに適合するかという宣言もできる。
この場合、コンパイル時に静的なチェックが出来るようになる。
プロトコルに適合しているかは、実装ではなくてインターフェース部でプロトコルを採用してるかを宣言子ておかないといけない。
-(void)setA:(NSView <Clickable> *)aView;
という宣言は、仮引数がNSView型のインスタンスというだけではなくて、カテゴリor継承でプロトコルClickableに適合したオブジェクトでないとエラーになる。

プロトコルへの適合チェック

プロトコルへの適合を動的に調べるには@protocol()ディレクトリを使用して、指定したプロトコルを表現するデータへのポインタを得て、conformsToProtocolメソッドを利用して判定できる。

必須機能

プロトコルのメソッドは@options,@requiredで必須かオプションなのかを指定することができる。(Objective-C2.0かららしいですよ)

非形式プロトコル

プロトコルという名前だけど実態はNSObjectへのカテゴリのこと
メソッド群で全てが必須ではない場合に非形式プロトコルが使われていたが、現在は@optionsなどが指定できるようになったので今はプロトコルを使ったものが多い。
非形式プロトコルの特徴としては
  • NSObjctクラスのカテゴリとして宣言
  • 宣言されたメソッドの実装は必須でない
  • コンパイル時に非形式プロトコルへの適合をチェックする方法はない
  • 実行時も同じく適合チェックできない。ここのメソッドを実装しているかは確認できない
という感じらしい。

CHAPTER10 抽象クラスとクラスクラスタ
2011/10/08(土) 26:22 Objective-Cはてブ情報 はてブに登録 はてブ数

抽象クラス

抽象クラスはサブクラスを定義することを前提としてメソッドの宣言のみをおこなっている不完全な形のクラスの事を抽象クラス、仮想クラスという。
Objective-Cでは抽象クラスのための仕組みはないので、あくまで概念。
allocすればインスタンスを作れてしまう。

クラスクラスタ

クラスクラスタは同じインターフェースと同じ機能を提供するクラスの集合体の事。
多くの場合抽象クラスとして実装されていて、インスタンスは抽象クラスのサブクラスが使われていたりするが、余り意識しなくても使える。
NSStringとかNSArrayとかもクラスクラスタなもの

クラスクラスタのサブクラスを作るには

クラスクラスタを拡張したり変更したクラスを作るにはどうするか?
カテゴリで対応する
カテゴリでクラスクラスタに機能を付けることができる。
プリミティブメソッドを最定義する。
クラスクラスタ内のクラス実装はここのクラスごとに異なる部分と、クラスタで共通の部分ある。
ここのクラスごとに異なる部分へアクセスを担当するメソッドをプリミティブメソッドと呼ぶ。
つまり、クラスクラスタの新しいサブクラスを作成するには、プライベートなデータ構造と、それにアクセスするプリミティブメソッドを定義すれば良いことになる。

クラスタのサブクラスを作成する手順
  1. プライベートなデータ構造を決める
  2. イニシャライザを定義する
  3. コンビニエンスコンストラクタを定義する
  4. プリミティブメソッドを定義する
  5. その他のメソッド定義する

CHAPTER09 カテゴリ
2011/09/23(金) 21:03 Objective-Cはてブ情報 はてブに登録 はてブ数

サブモジュールとしてのカテゴリ

カテゴリをサブモジュールとして、関係が深いものや用途が似ているものをグループにすることができる。

メソッドの前方宣言

インターフェースにメソッドが宣言されていなくても、C言語のように前方の局所メソッドは参照することができますが、これをカテゴリを使って解決することもできる。

このままだとmethodAからmethodBを呼ぶことができない。
- (int)methodA{
    [self methodB:0];
}
- (void)methodB:(int)arg{
    [self methodA];
}
これを局所的カテゴリを使って解決することもできる。
- (int)methodA{
    [self methodB:0];
}
- (void)methodB:(int)arg{
    [self methodA];
}

// 局所的カテゴリの宣言 

@interface クラス名(Local)
- (int)methodA;
- (void)methodB:(int)arg;
@end

@implementation クラス名(Local)
- (int)methodA{
    [self methodB:0];
}
- (void)methodB:(int)arg{
    [self methodA];
}
@end

プライベートなメソッド

クラスの外部には公開しないクラスにメソッドをまとめておいて、それをカテゴリとして利用できるようにすればプライベートなメソッドとして定義できる。
しかし、このカテゴリで定義したメソッドはサブカテゴリからメソッドを上書きできてしまうため、固有の接頭辞をつけたプライベートメソッドにすることが推奨されている。

クラスエクステンション

クラスをカテゴリに分けて宣言、実装を行った場合クラス本体とそれぞれをカテゴリは独立してコンパイルされるため、他にどんなカテゴリが付随しているのかは認識しない。つまりリンカでチェックされるわけではない。
カテゴリを別ファイルにして読みこむようにしてプライベートなメソッドを定義するという形の場合、
カテゴリのファイルの定義を実装し忘れてる心配があります(呼び出されるときには落ちるが、コンパイルできる)。
こういう時にクラスエクステンションといく特殊なカテゴリを使うと、実装漏れを防ぐ事ができる。
// クラスエクステンションは 無名カテゴリとも言われたり
@interface Card( )
- (void)methodA;
@end
このように非公開クラスファイルでカテゴリ宣言をすれば、実装しないとコンパイル時にエラーになります。(クラスエクステンション自体でプライベートにする機能があるわけじゃないので、非公開のクラスにクラスエクステンションを使った宣言を作る).

既存クラスへのカテゴリ追加

NSObjectなどの既存クラスへもカテゴリを使って、新しいメソッドをつけたりする事できる。
これはサブクラスに対してもメソッドが有効になるのでとても便利だが、乱用すると危険。
JavaScriptのネイティブオブジェクトのprototype拡張みたいなものだと思うので、ライブラリがやるのはやばそう。

既存メソッドの上書き

カテゴリを使えば、既存クラスのメソッドも上書きする事ができる。
だが、このクラスのサブクラスからsuperでメソッドを送ると、カテゴリの場合は完全に上書きしてるのでsuperとやっても再帰呼び出しが発生するだけになる。
またトラブルの元にもなるので、基本的にはやるべきではない。

連想参照

Appleのドキュメントだと関連参照
あるオブジェクトにたいして、別のオブジェクトへの参照を追加できる機能。
普通のクラス定義は、インターフェース部にインスタンス変数を宣言する事で、そのクラスのインスタンスはその変数を持つようになる。
これに対して連想参照は、実行中に必要に応じて「オブジェクトへの参照を追加」できるので、システム関数を呼び出してくっつけるような感じになる。
これによって、同じクラスのインスタンスでも、参照できるオブエジェクトなどを変更できる。

オブジェクトの関連付けと参照

別のオブジェクトを参照できる世にする関連付けを行う関数は
void objc_setAssociatedObject
id objc_getAssociatedObject
void objc_removeAssociatedObject
というのを使う。
removeはまとめて削除するため、setでnilをつけて個別に関連付けを外すこともできる。

例えば、配列から任意の要素を毎回ランダムで取り出すメソッドをカテゴリで定義したいとすると、NSArrayにそれをつけた場合に、前回とった要素を覚えておく必要があります。
その時に、これを連想参照とカテゴリを使って実現できる。
カテゴリなので、NSArrayのサブクラスからも利用できる。