NSSet
集合クラス
解説
「集合」と呼ばれるコレクションクラスの一つです。
配列クラス(NSArrayやそのサブクラス)に比べて以下のような特徴があります。
・順序がない
・同じ要素が複数含まれることがない。
継承 | NSObject |
準拠 | NSCopying |
NSMutableCopying | |
NSFastEnumeration | |
NSSecureCoding | |
NSObject (NSObject) | |
フレームワーク | /System/Library/Frameworks/Foundation.framework |
使用可能 | Mac OS X v10.0 以降 |
iOS 2.0 以降 | |
定義 | NSKeyValueCoding.h |
NSKeyValueObserving.h | |
NSPredicate.h | |
NSSet.h | |
NSSortDescriptor.h |
概要
要素の重複が無く、順序もないコレクションクラスです。iOSでなじみ深いのがtouchが返されてくるデリゲートメソッドでしょう。
このクラスは作成・初期化後変更できません。変更可能なセットオブジェクトはNSMutableSetクラスを使用します。
C言語のフレームワークである、Core FoundationのCFSetとトールフリーブリッジです。MRC環境以外ではそのまま使用できます。
どちらかのフレームワークにしかないメソッド・関数を使いたい場合、キャストして使うことが出来ます。
ARC環境では、管理対象のオブジェクトのキャストに__bridge、__bridge_retained、__bridge_transferが必要になります。
作成
NSSetオブジェクトを作成するにはいくつかの方法がありますが、配列(NSArray)からNSSetオブジェクトを作成するには(+ setWithArray)メソッドを使用します。
NSArrayは要素の重複が許されますので、要素がNSSetに追加される場合重複した要素はどうなるでしょうか?追加しようとする要素と同じ要素がすでにNSSetに存在する場合は追加せず、先の要素が残ります。同じ要素と言うのは内容が同じという意味で、別のオブジェクトであっても内容が同じであれば同じものだと見なされます。
オブジェクトからNSSetオブジェクトを作成するには(+ setWithObject)メソッドを使用します。複数のオブジェクトからNSSetオブジェクトを作成するには(+ setWithObjects)メソッドを使用します。C配列からNSSetを作成するメソッド(+ setWithObjects:count)も用意されています。
いずれも各要素に対してretainメッセージが送信されます。
C配列からNSSetを作る
#pragma mark NSSet setWithArray:count: -(void)method004 { NSString *strings[5]; strings[0]=@"Jan"; strings[1]=@"Feb"; strings[2]=@"Mar"; strings[3]=@"Apr"; strings[4]=@"May"; NSSet *aSet = [NSSet setWithObjects:strings count:3]; NSLog(@"%@",[aSet description]); //=>{(Jan,Feb,Mar)} [aSet enumerateObjectsUsingBlock:^(id obj,BOOL *stop) { NSLog(@"%s %p %@",__FUNCTION__,obj,[obj description]); }]; //=>0x6a70 Jan //=>0x6a80 Feb //=>0x6a90 Mar }
NSSetでは使いませんがNSMutableSetの作成のために(+ set)メソッドが用意されています。NSSetから別のNSSetを作って返すメソッド(+ setWithSet)もあります。
NSArrayから作成するとき、重複するオブジェクトはどうなるか?
#pragma mark NSSet setWithArray -(void)method003 { NSString *a1String = @"a"; NSString *bString = @"b"; NSString *cString = @"c"; NSString *a2String = [NSString stringWithFormat:@"%@",@"a"]; NSString *a3String = @"a"; NSLog(@"%s %p",__FUNCTION__,a1String); //=>0x6a10 NSLog(@"%s %p",__FUNCTION__,bString); //=>0x6a20 NSLog(@"%s %p",__FUNCTION__,cString); //=>0x6a30 NSLog(@"%s %p",__FUNCTION__,a2String); //=>0x6d6b1e0 NSLog(@"%s %p",__FUNCTION__,a3String); //=>0x6a30 NSArray *anArray = [NSArray arrayWithObjects:a1String,bString,cString,a2String,a1String,a3String, nil]; NSSet *aSet = [NSSet setWithArray:anArray]; NSLog(@"%@",[aSet description]); //=> [aSet enumerateObjectsUsingBlock:^(id obj,BOOL *stop) { NSLog(@"%s %p",__FUNCTION__,obj); }]; //=>0x6a20 //=>0x6a30 //=>0x6a10 }
元のNSSetオブジェクトにオブジェクトを追加して新しいNSSetを返すメソッド(– setByAddingObject)があります。配列(NSArray)を追加して新しいセットオブジェクトを返すメソッドが(– setByAddingObjectsFromArray)で、セットを追加して新しいセットオブジェクトを返すメソッドが(– setByAddingObjectsFromSet)です
Xcode4.4からリテラルが強化されました。NSNumberやNSArrayなどが今までよりも簡単な表記を使って書く事ができるようになっています。
NSNumber *value = [NSNumber numberWithInt:5];
と表記していたものが
NSNumber *value = @5;
NSNumber *value = [NSNumber numberWithUnsignedLong:12345ul];
と表記していたものが
NSNumber *value = @12345ul;
と表記できるようになっています。
コレクションクラスでは
NSArray *anArray = [NSArray arrayWithObjects:aString,bString,cString, nil];
と表記していたものが
NSArray *anArray = @[aString,bString,cString];
NSSetではこのような表記はありませんがNSArrayからNSSetを作る場合など簡単な表記で作る事ができるようになります。
この本のサンプルでは4.3以前のXcodeでも使えるように従来方式で表記していますが、しばらくすればこの新しい表記が一般的になるでしょう。
セットの初期化
allocで作成した後初期化するメソッドがあります。配列(NSArray)から初期化するメソッドが(– initWithArray)で、セット(NSSet)から初期化するメソッドが(– initWithSet)です。オブジェクトから初期化するメソッド(– initWithObjects)やC配列から初期化するメソッド(– initWithObjects:count)があります。
通常は要素をコピーせず、要素の参照のみを保持するのですが、要素をコピーしてその参照を保持するメソッド(– initWithSet:copyItems)があります。copyItems:にYESを渡します。
要素のカウント
配列(NSArray)と同様に要素数をカウントするメソッドは(– count)を使います。
セットの要素へのアクセス
セットに含まれる全ての要素を配列(NSArray)で返すには(– allObjects)メソッドを使います。セットに含まれるオブジェクトのどれか一つを返すには(– anyObject)メソッドを使います。
指定のオブジェクトがセットオブジェクトに含まれているかを調べるには(– containsObject)メソッドを使います。内容が同じであれば同じオブジェクトと判断されます。
NSPredicateオブジェクトを使ってフィルタリングしてオブジェクトを返すには(– filteredSetUsingPredicate)メソッドを使います。
フィルタリングした例
#pragma mark NSSet filteredSetUsingPredicate: -(void)method010 { NSArray *anArray = [NSArray arrayWithObjects:@"aaa",@"bbb", @"ccc", @"aaa",@"abc",@"これが",nil]; NSSet *aSet = [NSSet setWithArray:anArray]; NSPredicate *aPredicate1 = [NSPredicate predicateWithFormat:@"SELF IN %@",[NSArray arrayWithObjects:@"aaa", @"bbb", nil]]; NSPredicate *aPredicate2 = [NSPredicate predicateWithFormat:@"SELF LIKE %@",@"aaa"]; NSPredicate *aPredicate3 = [NSPredicate predicateWithFormat:@"SELF LIKE %@",@"s"]; NSPredicate *aPredicate4 = [NSPredicate predicateWithFormat:@"SELF LIKE %@",@"a*"]; NSPredicate *aPredicate5 = [NSPredicate predicateWithFormat:@"SELF LIKE %@",@"*が*"]; NSLog(@"%s %@",__FUNCTION__,[[aSet filteredSetUsingPredicate: aPredicate1 ] description]); //=>{(aaa, bbb)} NSLog(@"%s %@",__FUNCTION__,[[aSet filteredSetUsingPredicate: aPredicate2 ] description]); //=>{(aaa)} NSLog(@"%s %@",__FUNCTION__,[[aSet filteredSetUsingPredicate: aPredicate3 ] description]); //=>{()} NSLog(@"%s %@",__FUNCTION__,[[aSet filteredSetUsingPredicate: aPredicate4 ] description]); //=>{(aaa,abc)} NSLog(@"%s %@",__FUNCTION__,[[aSet filteredSetUsingPredicate: aPredicate5 ] description]); //=>{("\U3053\U308c\U304c")} }
各要素にメッセージを送信するメソッドは2つあり引数なしのメソッド(– makeObjectsPerformSelector)と引数1つのメソッド(– makeObjectsPerformSelector:withObject)です。
(– member)メソッドは指定したオブジェクトと同じ内容の要素を返します。isEqual:で比較してYESであれば同一の内容と見なされます。列挙のために列挙子(NSEnumerator)を返すには(– objectEnumerator)メソッドを使用します。
iOS4からはブロック構文が使えるようになり、列挙はNSSetのメソッドでできるようになりました。オプションなしのメソッドが(– enumerateObjectsUsingBlock)で、オプション付きのメソッドが(– enumerateObjectsWithOptions:usingBlock)です。オプションにNSEnumerationConcurrentを渡す事で並列処理を行います。
セットの各要素を列挙してなにか処理を行う
#pragma mark enumerateObjectsWithOptions:usingBlock: -(void)method015 { NSArray *anArray = [[NSArray alloc] initWithObjects: @"aaa",@"bbb",@"ccc", @"ddd",@"eee",@"fff", @"ggg",@"hhh",@"iii",nil]; NSSet *aSet = [NSSet setWithArray:anArray]; [aSet enumerateObjectsWithOptions:(NSEnumerationConcurrent | NSEnumerationReverse) usingBlock:^(id obj, BOOL *stop) { NSLog(@"%s %@",__FUNCTION__ ,[obj description]); //if ([obj isEqualToString:@"ggg"]) { // *stop = YES; //} }]; }
要素をテストしてYESのものだけを新しいセットオブジェクトとして返すメソッド(– objectsPassingTest)があります。オプション付きのメソッド(– objectsWithOptions:passingTest)を使うと並列処理ができます。
セットの比較
比較するセットはレシーバのサブセットかどうかを調べるには(– isSubsetOfSet)メソッドを使います。比較するセットとレシーバに重複する要素があるかどうかを調べるには(– intersectsSet)メソッドを使います。
比較するセットとレシーバが全く同じ要素を持つかどうかを調べるには(– isEqualToSet)メソッドを使います。
キーバリューコーディングで指定のキーを使ってセットの要素を取り出すには(– valueForKey)メソッドを使います。要素が変更可能なオブジェクトである場合は(– setValue:forKey)メソッドで内容の変更が行えます。