アーカイブ

‘たのしいCocoa’ タグのついている投稿

【たのしいCocoa】08_コレクション

2009-02-01

【たのしいCocoa】08_コレクション

たのしいCocoaプログラミング[Leopard対応版]」を元に、要約メモしておきたいと思います。

コレクション

複数のオブジェクトをまとめておくためのクラスのことを、コレクションクラスと呼ぶ。

配列

NSArray と NSMutableArray

例えば、NSString のインスタンスをまとめておく配列は次のようになる。

1
NSString* names[5];

配列の作成
1個のオブジェクトを指定して作成

1個だけオブジェクトを指定して配列を作る方法。arrayWithObject: というメソッドを使う。

NSArray

1
2
+ (id) arrayWithObject:(id)object
オブジェクト object を指定して、インスタンスを作成する

使用例は以下。

1
2
3
// 文字列1つを含む配列を作る
NSString* string = @"name";
NSArray* array = [NSArray arrayWithObject:string];

複数のオブジェクトを指定して作成

複数のオブジェクトを指定するには、arrayWithObjects:initWithObject: というメソッドを使う。

NSArray

1
2
3
 + (id) arrayWithObjects: (id) firstObj, ...
 - (id) initWithObjects: (id) firstObj, ...
 複数のオブジェクトを指定して、インスタンスを作成する。引数の最後には nil を指定する。

このメソッドは可変変数を使う。複数指定した引数の最後には必ず nil を指定する。nil を渡すことで、オブジェクトの引数はここまで、という宣言になる。

1
2
3
4
// 引数の文字列を指定して、配列を作成する
NSArray* array;
array = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
// array は、"A"、"B"、"C"を含む大きさ3の配列になる。

引数の最後の nil を忘れると、ほとんどの場合アプリケーションがクラッシュする。

配列の大きさ

配列の大きさを調べるには、count というメソッドを使う。

NSArray

1
2
- (unsigned) count
配列に含まれる要素の数を取得する

使用例は以下。

1
2
3
4
5
// 配列を作る
NSArray* array = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
// 配列の大きさを表示する
NSLog(@"count is %d, [array count]);
// count は、3になる

インデックスを指定したオブジェクトの取得

配列の中にあるオブジェクトを取り出すためには、objectAtIndex: というメソッドを使う。

NSArray

1
2
- (id) objectAtIndex:(unsigned)index
インデックス index を指定して、要素を取得する

例えば、3番目のオブジェクトを取り出すには以下のようになる。

1
2
3
4
5
6
7
// 配列を作る
NSArray* array = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
 
// 3番目のオブジェクトを取得する
NSString* thirdObject = [array objectAtIndex:2];
NSLog(thirdObject);
// thirdObject は、"C"になる

インデックスが配列の大きさを超えてしまうと例外が発生してしまう。それを防ぐには、オブジェクトを取り出す前に配列の大きさをチェックする。

1
2
3
4
5
6
7
8
// objectAtIndex: でアクセスする前に、count を使って配列の大きさをチェックする
// ここでは2より大きい、つまり3以上あるかどうか調べている
NSArray* array = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
int index = 2;
if ([array count] > index) {
	NSString* thirdObject;
    thirdObject = [array objectAtIndex:index];
  }

objectAtIndex: と for 文を利用すれば、配列の中にあるすべてのオブジェクトにアクセスすることができる。

1
2
3
4
5
6
7
8
9
10
// 配列を作る
NSArray* array = [NSArrayWithObjects:@"A", @"B", @"C", nil];
 
// 配列の大きさだけ、 for ループを回す
int i;
for (i = 0; i < [array count]; i++) {
// インデックスを指定して、オブジェクトを取り出す
NSString* string = [array objectAtIndex:i];
NSLog(string);
}

for 文の中で、count メソッドを使って配列の大きさをチェックしている。実行すると i は0から2まで変化することになる。それぞれの値で objectAtIndex: を呼び出すから全てのオブジェクトを取り出すことができる。

NSEnumerator を使ったオブジェクトの取得

for 文を使う以外に、列挙子と呼ばれる考え方を使うものもある。
NSEnumerator

1
2
- (id)nextObject
現在のオブジェクトの、次のオブジェクトを取得する

NSEnumerator を取得したら、このメソッドを呼び続ける。そうして全てのオブジェクトを次々と取り出せる。
NSArray で NSEnumerator を利用する場合は、objectEnumerator というメソッドを使う。

NS Array

1
2
- (NSEnumerator*)objectEnumerator
列挙子を取得する

取得したら、while 文を使ってループを回ることになる。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 配列を作る
NSArray* array = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
 
// 列挙子を取得する
NSEnumerator* enumerator;
enumerator = [array objectEnumerator];
 
// while 文を使って、NSEnumerator の nextObject を繰り返し呼び出す。
NSString* string;
while (string = [enumerator nextObject]) {
// 取り出した文字列に対して処理を行う
NSLog(string);
}

nextObject メソッドを使ってオブジェクトを取り出して、それを string に代入している。その string の値が、while ループを続けるかどうかの条件になっている。値が入っていればループは続き、最後のオブジェクトになって nil が返ってくればループは終了する。

高速列挙を使ったオブジェクトの取得

1
2
3
4
5
6
7
8
// 配列を作る
NSArray* array = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
 
// 高速列挙を使って、オブジェクトにアクセスする
for (NSString* string in array) {
// 取り出した文字列に対して処理を行う
NSLog(string);
}

for 文に対して拡張が行われていて、直接配列にアクセスすることができる。文法的には以下のようになる

1
2
3
for (変数宣言 in オブジェクトの集合) {
...
}

for 文の中で変数の宣言を行い、in というキーワードを置く。その後にオブジェクトの集合となるクラスがくる。オブジェクトの集合としては NSArray などを指定することができる。高速列挙は、NSWnumerator を使うものと比べると、ソースコードの記述量が格段に少なくなる。

オブジェクトの追加と削除
空の配列を作成

空の配列を作っておいて、あとからオブジェクトを追加することもできる。

NSArray

1
2
+ (id)array
中身が空のインスタンスを作成する

オブジェクトの追加

オブジェクトを追加するためのメソッドは、NSMutableArray で定義されている。

NSMutableArray

1
2
3
4
5
6
7
8
9
10
- (void)addObject:(id)object
オブジェクト object を追加する
- (void)addObjectsFormArray:(NSArra*)array
配列 array に含まれている全てのオブジェクトを追加する
- (void)insertObject:(id)object atIndex:(unsigned)Index
オブジェクト object をインデックス index に追加する
- (void)replaceObjectAtIndex:(unsigned)index withObject:(id)object
インデックス index にあるオブジェクトを、オブジェクト object と置換する
- (void)setArray:(NSArray*)array
すべてのオブジェクトを、配列 array に含まれているオブジェクトと交換する

インデックスを指定するものは、配列を越えると例外が発生してしまうので注意。使用例は以下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 空配列を作る
NSMutableArray* array = [NSmutableArray array];
 
// オブジェクトを追加する
[array addObject:@"A"];
 
// 配列に含まれるオブジェクトを追加する
NSArray* array1 = [NSArray arrayWithObjects:@"B", @"C", @"D", nil];
[array addObjectsFromArray:array1];
 
// オブジェクトを挿入する
[array insertObjects:@"E" atIndex:0];
 
// オブジェクトを置き換える
[array replaceObjectAtIndex:1 withObject:@"F"];
 
NSLog(@"%@", array);
// 最終的な配列は、"E"、"F"、"B"、"C"、"D"

オブジェクトの削除

NSMutableArray

1
2
3
4
5
6
7
8
9
10
11
12
- (void)removeAllObjects
すべてのオブジェクトを削除します
- (void)removeLastObject
最後のオブジェクトを削除する
- (void)removeObjectAtIndex:(unsigned)index
インデックス index にあるオブジェクトを削除する
- (void)removeObject:(id)object
配列に含まれるオブジェクトで、オブジェクト object と同じ値を持つものをすべて削除する
- (void)remobeObjectIdentialTo:(id)object
配列に含まれるオブジェクトで、オブジェクト object と同じものをすべて削除する
- (void)removeObjectsInArray:(NSArray*)array
配列に含まれるオブジェクトで、配列 array に含まれるオブジェクトと同じ値を持つものをすべて削除する

辞書

辞書とは

あるオブジェクトを指定すると、それに関連づけられたオブジェクトを指定することができる。たとえば、ある Mac の情報をこのようにまとめておける。

1
"CPU" => "Intel Core 2 Duo"

CPU がキーで、Intel Core 2 Duo が

NSDictionary と NSMutableDictionary

Cocoa で辞書を実現するのは、変更できない辞書 NSDictionary と、変更できる辞書 NSMutableDictionary というクラス。

NSDictionary の作成
1つのキーの値のペアを指定して作成

NSDictionary

1
2
+ (id)dictionaryWithObject:(id)object forKey:(id)key
値 object とキー key を指定して、インスタンスを作成する

使用例は以下。

1
2
3
// キーと値のペアを指定して辞書を作る
NSDictionary* dict;
dict = [NSDictionaryWithObject:@"Core 2 Duo" forKey:@"CPU"];

複数のキーと値のペアを指定して作成

NSDictionary

1
2
3
4
5
6
+ (id)dictionaryWithObjects:(NSArray*)objects forKeys:(NSArray*)keys
- (id)initWithObjects:(NSArray*)objects forKeys:(NSArray*)keys
値の配列 objects とキーの配列 keys を指定して、インスタンスを作成する
+ (id)dictionaryWithObjectsAndKeys:(id)firstObj, ...
- (id)initWithObjectsAndKeys:(id)firstObj, ...
値とキーのペアを複数指定して、インスタンスを作成する。最後の引数は nil にする。

使用例は以下。

1
2
3
4
5
6
7
8
9
10
11
// キーと値の配列を指定して辞書を作る
NSArray* keys;
NSArray* objects;
NSDictionary* dict0;
keys = [NSArray arrayWithObjects:@"CPU", @"Memory", @"Color", nil];
objects = [NSArray arrayWithObjects:"Core 2 Duo", @"2GB", @"Black", nil];
dict0 = [NSDictionary dictionaryWithObjects:ojects forKeys:keys];
 
// キーと値のペアを引数として指定して、辞書を作る
NSDictionary* dict1;
dict1 = [NSDictionary dictionaryWithObjectsAndKeys:@"Core 2 Duo", @"CPU", @"2GB", @"Memory", @"Black", @"Color", nil];

キーと値の取得
キーを指定して値を取得

作った辞書を引くためには、objectForKey: というメソッド。

NSDictionary

1
2
- (id)objectForKey:(id)key
キー key を指定して、関連づけられている値を取得する。対応する値がない時は nil を返す。

使用例は以下。

1
2
3
4
5
6
// 辞書を作る
NSDictionary* dict = [NSDictionary dictionaryWithOjectAndKeys:@"Core 2 Duo", @"CPU", @"2GB", @"Memory", @"Black", @"Color", nil];
 
// キー"CPU"を取得して、対応する値を取り出す
NSString* cpuType = [dict objectForKey:@"CPU"];
// cpuTypeには、"Core 2 Duo"が入る

すべてのキー、値を取得

辞書に含まれるすべてのキーと値を取得することもできる。

NSDictionary

1
2
3
4
5
6
7
8
- (NSArray*)allkeys
すべてのキーを配列の形で取得する
- (NSEnumerator*)keyEnumerator
すべてのキーにアクセスする列挙子を取得する
- (NSArray*)allValues
すべての値を配列の形で取得する
- (NSEnumerator*)objectEnumerator
すべての値にアクセスする列挙子を取得する

使用例は以下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 辞書を作る
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:@"Core 2 Duo", @"CPU", @"2GB", @"Memory", @"Black", @"Color" nil];
 
// キーのための列挙子を取得する
NSEnumerator* enumerator = [dict keyEnumerator];
// すべてのキーを取り出す
NSString* key;
while (key = [enumerator nextObject]) {
// キーに対する値を取得する
NSString* value;
value = [dict objectForKey:key];
 
// キーと値のペアに対する処理
NSLog(@"key is %@, value is %@", key, value);
}

まず keyEnumerator メソッドで列挙子を取得して、while 文を回す。これでキーを順々に取り出すことができる。

キーと値の追加と削除
空の辞書を作成

NSDictionary

1
2
+ (id)dictionary
中身が空の辞書を作成する

キーと値のペアを追加

NSMutableDictionary

1
- (void)setObject:(id)object forKey:(id)key

どちらの値も nil にならないように気をつける。使用例は以下。

1
2
3
4
5
6
7
// 空の辞書を作る
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
 
// キーと値をペアで追加する
[dict setObject:@"Core 2 Duo" forKey:@"CPU";
[dict setObject:@"2GB" forKey:@"Memory";
[dict setObject:@"Black" forKey:@"Color";

キーと値のペアを削除

NSMutableDictionary

1
2
3
4
5
6
- (void)removeAllObjects
すべてのキーと値のペアを削除する
- (void)removeObjectForKey:(id)key
キー key と、それに関連づけられているオブジェクトを削除する
- (void)removeObjectsForKeys:(NSArray*)keyArray
キーの配列 keyArray と、それに含まれているキーに関連づけられているオブジェクトを削除する

ラッパークラス

ラッパークラスとは

NSArray と NSDictionary という2つのコレクションクラスに入れることができるのは、オブジェクトになる。整数や小数や理論値などの数値を入れるには、int や float や BOOL といった基本型を使う。ラッパークラスでこれらの基本型を包んで、オブジェクトとして扱えるようにするクラス。

NSNumber

基本型からインスタンスを作るためのメソッド。

NSNumber

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+ (id)numberWithBool:(BOOL)value
- (id)initWithBool:(BOOL)value
理論値型の値 value を指定してインスタンスを作成する
+ (id)numberWithChar:(char)value
- (id)initWithChar:(char)value
+ (id)numberWithUnsignedChar:(unsigned char)value
- (id)initWithUnsignedChar:(unsigned char)value
文字型の値 value を指定してインスタンスを作成する
+ (id)numberWithInt:(int)value
- (id)initWithInt:(int)value
+ (id)numberWithUnsignedInt:(unsigned int)value
- (id)initWithUnsignedInt:(unsigned int)value
+ (id)numberWithLong:(long)value
- (id)initWithLong:(long)value
+ (id)numberWithUnsignedLong:(unsigned long)value
- (id)initWithUnsignedLong:(unsigned long)value
整数型の値 value を指定して、インスタンスを作成する
+ (id)numberWithFloat:(float)value
- (id)initWithFloat:(float)value
+ (id)numberWithDouble:(double)value
- (id)initWithDoouble:(double)value
小数型の値 value を指定してインスタンスを作成する

取り出す方のメソッドは以下。

NSNumber

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (BOOL)boolvalue
論理値型の値を取得する
- (char)charValue
- (unsigned char)unsingnedCharValue
文字型の値を取得する
- (int)intValue
- (unsingned int)unsingnedIntValue
- (long)longValue
- (unsigned long)unsignedLongValue
整数型の値を取得す
- (float)floatValur
- (double)doubleValue
小数型の値を取得する
- (NSString*)stringValue
値を文字列にしたものを取得する

これらの値を取り出すメソッドは、インスタンスを作成したときの基本型によらず、どの型としてでも取り出すことができる。また、stringValue を使うと、今の値を文字列として取り出すことができる。

1
2
3
4
5
6
7
8
9
10
// 空の文字列を作る
NSMUtableArray* array = [NSMutableArray array];
 
int i;
for (i=0; i<0; i++) {
// 整数型を指定して、NSNumber クラスを作る
NSNumber* number = [NSNumber numberWithInt:i];
// 配列に追加する
[array addObject:number];
}

オブジェクトの同等性と同値性

同じ値を持つオブジェクト

異なるインスタンスでありながら同じ値を持っていることを同値である、インスタンス自体が同じであることを同等であるという。

同値性と同等性の判定

同等性のチェックは比較演算子 == を使う。

1
2
3
4
5
6
7
// オブジェクトの同等性をチェックする
id objectA, objectB;
objectA = @"stringA";
objectB = objectA;
if (objectA == objectB) {
// objectA と objectB は同等
}

同値性は isEqual: というメソッドを使う。

NSObject

1
2
- (BOOL)isEqual:(id)object
オブジェクト object が、このインスタンスと等しいかを返す。

使用例は以下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// NSArray のインスタンスを2つ作る
NSArray* array0 = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
NSArray* array1 = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];
 
// array0 と array1 の同等性を調べる
BOOL result;
result = array0 == array1;
NSLog(@"array == array1, %d, result);
// result は NO になる
 
// array0 とarray1 の同値性を調べる
result = [array0 isEqual:array1];
NSLog(@"[array0 isEqual:array1], %d, result);
// result は YES になる

同等ではないが同値であるということになる。

コレクションクラスと参照カウンタ

コレクションクラスにオブジェクトを追加すると、コレクションクラスがそのオブジェクトを保持することになり、参照カウンタが1上がる。

NSArray と参照カウンタ

NSArray が開放されるときには、そこに登録されているオブジェクト全てに release が送られる。一度 NSArray に入れれば、そのオブジェクトを保持する必要があったとしても release しても構わない。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 空の配列を作成する
// この array という変数は、インスタンス変数として宣言されているものとする
array = [NSMutableArray array];
 
// NSString インスタンスを作る
NSString* string = [[NSString alloc] iniWithCString:"New String"encoding:NSASCIIStringEncoding];
// この時点で参照カウンタは1
 
// 配列に追加する
[array addObject:string];
// string の retain メソッドが呼び出される。参照カウンタは2になる
 
// string の release メソッドを呼び出す
[string release];
// 参照カウンタは1になる
// これで NSArray がこのオブジェクトの開放に責任を持つことになる

NSDictionary と参照カウンタ

値に関しては NSArray と同じ。NSDictionary に追加された時点で retain される。キーに関しては少し違って、NSDictionary に追加しようとすると、キーのオブジェクトはコピーされて、コピーされた方が追加される。

,