NSDictionary用于儲存具有映射關系的資料。是以,NSDictionary集合裡儲存着兩組值,一組值用于儲存NSDictionary裡的key,另一組值用于儲存NSDictionary裡的value。注意:key和value都可以是任何指針類型的資料,NSDictionary的key不允許重複。
key和value之間存在一對一關系即通過指定的key,總能找到唯一的、确定的value。從NSDictionary中取出資料時,隻要給出指定的key,就可以取出對應的value。NSDictionary有類似下圖的結構:
所有的key組成一個NSSet集合(key沒有順序,不能重複),實際上,NSDictionary确實包含一個allKeys方法,用于傳回NSDictionary所有key組成的集合,但是,NSDictionary把allKeys方法的傳回值設為NSArray,說明該方法經過了進一步的轉換。
NSDictionary的功能與用法
NSDictionary分别提供了類方法和執行個體方法來建立NSDictionary,類方法以dictionary開頭,執行個體方法則以init開頭:
- dictionary:建立一個不包含任何key-value對的NSDictionary。
- dictionaryWithContentsOfFile:/initWithContentsOfFile::讀取指定檔案的内容,使用指定的檔案内容來初始化NSDictionary。該檔案通常是由NSDictionary輸出生成的。
- dictionaryWithDictionary:/initWithDictionary::使用已有的NSDictionary包含的key-value對來初始化NSDictionary對象。
- dictionaryWithObject:forKey::使用單個key-value對來建立NSDictionary對像。
- dictionaryWithObjects:forKeys:/initWithObjects:forKeys::使用兩個NSArray分别指定key、value集合,可以建立包含多個key-value對的NSDictionary。
- dictionaryWithObjectsAndKeys:/initWithObjectsAndKeys::調用該方法時,需要按value1,key1,value2,key2,…,nil的格式傳入多個key-value對。
除此之外,還可以使用如下簡化文法來建立NSDictionary對象:
常用的通路該集合的key和value的方法有:
- count:傳回所有key-value對數量。
- allKeys:傳回全部key。
- allKeysForObject::傳回指定value對應的全部key。
- allValues:傳回包含的全部value。
- objectForKey::擷取指定key對應value。
- objectForKeyedSubscript::通過該方法的支援,允許通過下标法來擷取指定key對應的value。
- valueForKey::擷取指定key對應的value。
- keyEnumerator:傳回用于周遊該NSDictionary所有key的NSEnumerator對象。
- objectEnumerator:該方法傳回用于周遊該NSDictionary所有value的NSEnumerator對象。
- enumerateKeysAndObjectsUsingBlock::使用指定的代碼塊來疊代執行該集合中所有的key-value對。
- enumerateKeysAndObjectsWithOptions:usingBlock::使用指定的代碼塊來疊代執行該集合中所有的key-value對。可以傳入一個額外的NSEnumerationOptions參數。
- writeToFile:atomically::将該NSDictionary對象的資料寫入指定檔案。
示例程式:
該程式為了能更清晰地看到key-value對的詳情,為NSDictionary類擴充了一個print類别,在該類别中擴充了一個print方法,用于列印NSDictionary中key-value對的詳情。
該類接口部分:
#import <Foundation/Foundation.h>
@interface NSDictionary (print)
-(void) print;
@end
實作部分:
#import "NSDictionary+print.h"
@implementation NSDictionary (print)
-(void) print {
NSMutableString* result = [NSMutableString stringWithString:@"{"];
//快速枚舉法周遊
//循環計數器将依次等于每個key
for(id key in self) {
[result appendString: [key description]];
[result appendString:@"="];
//使用下标法根據key擷取對應的vakue
[result appendString:[self[key] description]];
[result appendString:@", "];
}
//擷取字元串長度
NSUInteger len = [result length];
//去掉字元串最後兩個字元
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"}"];
NSLog(@"%@", result);
}
@end
通過key來擷取value有兩種方法:
- 調用NSDictionary的objectForKey:方法
- 直接使用下标法
也就是說,下面兩行代碼的功能是相同的:
[dictionary objectForKey:key];
dictionary[key];
測試:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
[[User alloc] initWithName:@"Billy" andPass:@"1234"],@"one",
[[User alloc] initWithName:@"Terasa" andPass:@"2345"],@"two",
[[User alloc] initWithName:@"Amy" andPass:@"3456"],@"three",
[[User alloc] initWithName:@"John" andPass:@"4567"],@"four",
[[User alloc] initWithName:@"Jimmy" andPass:@"5678"],@"five",
nil];
[dict print];
NSLog(@"dict包含%ld個key-value對", [dict count]);
NSLog(@"dict的所有key為:%@", [dict allKeys]);
NSLog(@"<User[name=Billy,pass=1234]>對應的key為:%@",[dict allKeysForObject:[[User alloc] initWithName:@"Billy" andPass:@"1234"]]);
//擷取周遊dict所有value的枚舉器
NSEnumerator* en = [dict objectEnumerator];
id value;
//使用枚舉器來周遊dict中所有value
while(value = [en nextObject]) {
NSLog(@"%@", value);
}
//使用指定代碼塊來疊代執行該集合中所有的key-vakue對
[dict enumerateKeysAndObjectsUsingBlock:
//該集合包含多少個key-value對,下面的代碼塊就執行相應的次數
^(id key, id value, BOOL* stop) {
NSLog(@"key的值為:%@",key);
[value say:@"Bill"];
}];
}
return 0;
}
輸出:
{one=<User[name=Billy,pass=1234]>, five=<User[name=Jimmy,pass=5678]>, three=<User[name=Amy,pass=3456]>, two=<User[name=Terasa,pass=2345]>, four=<User[name=John,pass=4567]>}
dict包含5個key-value對
dict的所有key為:(
one,
five,
three,
two,
four
)
<User[name=Billy,pass=1234]>對應的key為:(
one
)
<User[name=Billy,pass=1234]>
<User[name=Jimmy,pass=5678]>
<User[name=Amy,pass=3456]>
<User[name=Terasa,pass=2345]>
<User[name=John,pass=4567]>
key的值為:one
Billy說:Bill
key的值為:five
Jimmy說:Bill
key的值為:three
Amy說:Bill
key的值為:two
Terasa說:Bill
key的值為:four
John說:Bill
對NSDictionary的key排序
方法如下:
- keysSortedByValueUsingSelector::根據所有value指定方法的傳回值對key排序;調用該方法必須傳回NSOrderedAscending、NSOrderedDescending、NSOrderedSame這三個枚舉值之一。
- keysSortedByValueUsingComparator::使用指定的代碼塊來周遊key-value對,并根據執行結果(必須傳回NSOrderedAscending、NSOrderedDescending、NSOrderedSame這三個枚舉值之一)對所有key進行排序。
- keysSortedByValueWithOptions:usingComparator::與前一個方法的功能類似,隻是可以傳入一個額外的NSEnumerationOptions參數。
示例程式:
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//
NSDictionary* dict = @{@"one": @"Billy",
@"two":@"Teresa",
@"three":@"Amy",
@"four":@"John"
};
//列印dict集合所有元素
[dict print];
//擷取所有直接調用value的compare:方法對所有key進行排序
//傳回排序好的所有key組成的NSArray
NSArray* keyArr1 = [dict keysSortedByValueUsingSelector:@selector(compare:)];
NSLog(@"%@", keyArr1);
NSArray* keyArr2 = [dict keysSortedByValueUsingComparator:
//對value進行比較,字元串越長,即可認為value越大
^(id value1, id value2){
//下面定義比較大小的标準:字元串越長,即可認為value越大
if([value1 length] > [value2 length]) {
return NSOrderedDescending;
} else if ([value1 length] < [value2 length]) {
return NSOrderedAscending;
} else {
return NSOrderedSame;
}
}];
NSLog(@"%@", keyArr2);
//将NSDictionary的内容輸出到指定檔案中
[dict writeToFile:@"mydict.txt" atomically: YES];
}
}
先使用compare:方法排序——字元串比較大小直接根據字元對應的編碼進行。後面調用代碼塊對value比較大小,規則是value對應的字元串越長,系統就認為該value越大。程式的輸出結果為:
{one=Billy, three=Amy, two=Teresa, four=John}
(
three,
one,
four,
two
)
(
three,
four,
one,
two
)
打開 mydict.txt 檔案,可以看到如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>four</key>
<string>John</string>
<key>one</key>
<string>Billy</string>
<key>three</key>
<string>Amy</string>
<key>two</key>
<string>Teresa</string>
</dict>
</plist>
對NSDictionary的key進行過濾
NSDictionary還提供了方法對NSDictionary的所有key執行過濾,這些方法執行完将傳回滿足條件的key組成的NSSet。方法如下:
- keysOfEntriesPassingTest::使用代碼塊疊代處理NSDictionary的每個key-value對。對NSDictionary的key-value對進行過濾,該代碼塊必須傳回BOOL類型的值,隻有當該代碼塊傳回YES時,該key才會被保留下來;該代碼塊可以接受3個參數,其中第一個參數代表正在疊代處理的key,第二個參數代表正在疊代處理的value,第三個參數代表是否還要繼續疊代,如果将第三個參數設為NO,那麼該疊代會立即停止。
- keysOfEntriesWithOptions:passingTest::該方法的功能與前一個方法的功能基本相同。隻是該方法可以額外傳入一個附加的NSEnumerationOptions選項參數。
如下程式示範了對NSDictionary進行過濾。
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary* dict = @{
@"Billy":[NSNumber numberWithInt: 89],
@"Teresa":[NSNumber numberWithInt: 69],
@"Amy":[NSNumber numberWithInt: 78],
@"John":[NSNumber numberWithInt: 109]
};
[dict print];
//對NSDictionary的所有key進行過濾
NSSet* keySet = [dict keysOfEntriesPassingTest:
//使用代碼塊對NSDictionary的key-value對進行過濾
^(id key, id value, BOOL* stop) {
//當value的值大于80時傳回YES
//這意味着隻有value的值大于80的key才會被儲存下來
return (BOOL)([value intValue] > 80);
}];
NSLog(@"%@", keySet);
}
}
輸出:
{John=109, Billy=89, Amy=78, Teresa=69}
{(
Billy,
John
)}
使用自定義類作為NSDictionary的key
如果程式打算使用自定義類作為NSDictionary的key,則該自定義類必須滿足如下需求:
- 該自定義類正确重寫過isEqual: 和hash方法。所謂正确重寫,是指當兩個對象通過isEqual:方法判斷相等時,兩個對象的hash方法傳回值也相等。
- 該自定義類必須實作了copyWithZone:方法,該方法最好傳回該對象的不可變副本。
為什麼要實作copyWithZone:方法呢?因為對于NSDictionary來說,key是非常關鍵的,NSDictionary需要根據key來通路value——從這個意義上看,key相當于NSDictionary的索引,如果key本身是可變的,且程式可以通過其他變量來修改NSDictionary的key,這就可能導緻NSDictionary的“索引”值被破壞,進而導緻NSDictionary的完整性被破壞。
為了讓前面的User類作為NSDictionary的key,還需要讓該User類實作NSCopying協定(可選的,通常建議實作),并讓該User類實作copyWithZone:方法。User實作的copyWithZone:方法如下:
-(id) copyWithZone: (NSZone*) zone {
NSLog(@"--正在複制--");
//複制一個對象
User* newUser = [[[self class] allocWithZone:zone] init];
//将被複制的對象的執行個體變量的值賦給新對象的執行個體變量
newUser->_name = _name;
newUser->_pass = _pass;
return newUser;
}
User.h:
#import <Foundation/Foundation.h>
@interface User : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* pass;
-(id) initWithName: (NSString*) aName andPass: (NSString*) aPass;
-(void) say: (NSString*) content;
@end
User.m:
#import "User.h"
@implementation User
-(id) initWithName:(NSString *) name andPass:(NSString *) pass {
if (self = [super init]) {
self->_name = name;
self->_pass = pass;
}
return self;
}
-(void) say: (NSString*) content {
NSLog(@"%@說:%@",self.name, content);
}
//重寫isEqual: 方法,重寫比較方法為
//如果兩個User的name、pass相等,既可以認為它們相等
-(BOOL) isEqual: (id) other {
if(self == other) {
return YES;
}
if([other class] == User.class) {
User* target = (User*) other;
return [self.name isEqualToString: target.name] && [self.pass isEqualToString: target.pass];
}
return NO;
}
//重寫description方法,可以看到User狀态
-(NSString*) description {
return [NSString stringWithFormat:@"<User[name=%@,pass=%@]>", self.name, self.pass];
}
-(NSUInteger) hash {
NSLog(@"===hash===");
NSUInteger nameHash = self.name == nil ? 0 : [self.name hash];
NSUInteger passHash = self.pass == nil ? 0 : [self.pass hash];
return nameHash *31 + passHash;
}
-(id) copyWithZone: (NSZone*) zone {
NSLog(@"--正在複制--");
//複制一個對象
User* newUser = [[[self class] allocWithZone:zone] init];
//将被複制的對象的執行個體變量的值賦給新對象的執行個體變量
newUser->_name = _name;
newUser->_pass = _pass;
return newUser;
}
@end
主函數:
#import "User.h"
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
User* u1 = [[User alloc] initWithName:@"Billy" andPass:@"765"];
NSDictionary* dict = @{
[[User alloc] initWithName:@"Bill" andPass:@"123"]:@"one",
u1:@"two",
[[User alloc] initWithName:@"Bill" andPass:@"123"]:@"three",
[[User alloc] initWithName:@"Nick" andPass:@"890"]:@"four",
[[User alloc] initWithName:@"Polly" andPass:@"761"]:@"five"
};
//将u1的密碼設為nil
u1.pass = nil;
//由于NSDictionary并未直接使用u1所指的User作為key
//而是先複制了u1所指向對象的副本,然後以該副本作為key
//是以程式将看到dict的key不會受到任何影響
[dict print];
}
}
當程式嘗試使用任何對象作為key時,會先調用copy方法來複制該key的不可變副本,實際是以該副本作為NSDictionary的key。是以,上面程式對u1的pass進行修改時,NSDictionary的所有key并不會受到任何影響。
輸出結果:
===hash===
--正在複制--
===hash===
--正在複制--
===hash===
===hash===
--正在複制--
===hash===
--正在複制--
===hash===
===hash===
===hash===
===hash===
{<User[name=Bill,pass=123]>=one, <User[name=Billy,pass=765]>=two, <User[name=Polly,pass=761]>=five, <User[name=Nick,pass=890]>=four}
其中兩個User對象的name、pass完全相同,但這兩個對象通過isEqual:方法會傳回YES,并且hash方法傳回值也相等,是以隻會保留一個。
NSMutableDictionary的功能與用法
NSMutableDictionary繼承了NSDictionary,它代表一個key-value對可變的NSDictionary集合。由于NSMutableDictionary可以動态地添加key-value對,是以,建立NSMutableDictionary集合時可以指定初始容量。
NSMutableDictionary主要新增了如下方法:
- setObject:forKey::設定一個key-value對。如果NSDictionary中沒有包含與該key相同的key-value對,那麼NSDictionary将會新增一個key-value對;否則該key-value對将覆寫已有的key-value對。
- setObject:forKeyedSubscript::通過該方法的支援,允許程式通過下标法來設定key-value對。
- addEntriesFromDictionary::将另一個NSDictionary中所有的key-value對複制到目前NSDictionary中。
- setDictionary::用另一個NSDictionary中所有的key-value對替換目前NSDictionary中的key-value對。
- removeObjectForKey::根據key來删除key-value對。
- removeAllObjects:清空該NSDictionary。
- removeObjectsForKeys::使用多個key組成的NSArray作為參數,同時删除多個key對應的key-value對。
示範程式:
int main(int argc, const char * argv[]) {
@autoreleasepool {
//使用單個key-value對來建立NSMutable Dictionary對象
NSMutableDictionary* dict = [NSMutableDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:89],@"Billy", nil];
//使用下标法設定key-value對。由于NSDictionary中已經存在該key
//是以此處設定的value會覆寫前面的value
dict[@"Billy"] = [NSNumber numberWithInt:99];
[dict print];
NSLog(@"再次添加key-value對");
dict[@"Amy"] = [NSNumber numberWithInt: 67];
[dict print];
NSDictionary* dict2 = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:79],@"Amy",
[NSNumber numberWithInt:86],@"Takumi",
[NSNumber numberWithInt:12],@"Jimmy",
nil];
//将另一個NSDictionary中的key-value對添加到目前NSDictionary中
[dict addEntriesFromDictionary: dict2];
[dict print];
//根據key來删除key-value對
[dict removeObjectForKey:@"Takumi"];
[dict print];
//同時删除多個key對應的key-value對
[dict removeObjectsForKeys:[NSArray arrayWithObjects:@"Amy",@"Billy", nil]];
[dict print];
}
}
輸出:
{Billy=99}
再次添加key-value對
{Billy=99, Amy=67}
{Jimmy=12, Billy=99, Takumi=86, Amy=79}
{Jimmy=12, Billy=99, Amy=79}
{Jimmy=12}