| 類別 | 可變性 | 繼承 | 適用場景 |
|---|---|---|---|
NSDictionary | 不可變 | NSObject | 建立後不再修改,執行緒安全、效能最佳 |
NSMutableDictionary | 可變 | NSDictionary | 需動態增刪改查,單執行緒或加鎖使用 |
兩者皆為類別簇,實際實例為私有子類 (如 `__NSDictionaryI`, `__NSDictionaryM`)。
// 1. 字面量語法 (現代、推薦)
NSDictionary *dict1 = @{ @"name": @"hechen", @"age": @25 };
// 2. 便利建構子
NSDictionary *dict2 = [NSDictionary dictionaryWithObjectsAndKeys:
@"hechen", @"name",
@25, @"age",
nil];
// 3. 逐對建立
NSDictionary *dict3 = [NSDictionary dictionaryWithObject:@"hechen" forKey:@"name"];
// 4. 從陣列建立 (鍵值陣列長度必須相同)
NSArray *keys = @[@"name", @"age"];
NSArray *objs = @[@"hechen", @25];
NSDictionary *dict4 = [NSDictionary dictionaryWithObjects:objs forKeys:keys];
NSUInteger count = [dict count]; // 元素個數
id value = [dict objectForKey:@"name"]; // 取值 (無則回傳 nil)
NSArray *allKeys = [dict allKeys]; // 所有鍵 (順序不定)
NSArray *allValues = [dict allValues]; // 所有值
// 快速枚舉 (推薦)
for (id key in dict) {
NSLog(@"%@ -> %@", key, dict[key]); // 下標語法等同 objectForKey:
}
// 過濾
NSPredicate *pred = [NSPredicate predicateWithFormat:@"key BEGINSWITH 'a'"];
NSDictionary *filtered = [dict filteredDictionaryUsingPredicate:pred];
// 寫入檔案 (Property List 格式,僅支援 Property List 型別)
[dict writeToFile:@"/tmp/config.plist" atomically:YES];
// JSON 序列化 (需值為 JSON 相容型別)
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:nil];
// 預留初始容量,減少擴容次數
NSMutableDictionary *md = [NSMutableDictionary dictionaryWithCapacity:100];
// 實際容量會自動成長,無需擔心越界
// 新增/修改 (鍵存在則覆蓋)
[md setObject:@"China" forKey:@"country"];
[md setObject:@100 forKey:@"score"];
// 批次新增
[md addEntriesFromDictionary:@{@"k1": @1, @"k2": @2}];
// 刪除單一鍵
[md removeObjectForKey:@"score"];
// 批次刪除
[md removeObjectsForKeys:@[@"k1", @"k2"]];
// 清空
[md removeAllObjects];
// 修改值 (Key-Value Coding)
[md setValue:@200 forKey:@"score"]; // 自動處理 nil -> 刪除鍵
// ❌ 錯誤:邊遍歷邊刪除會崩潰
// for (id key in md) { if ([key isEqualToString:@"bad"]) [md removeObjectForKey:key]; }
// ✅ 正確:先收集要刪除的鍵
NSMutableArray *keysToRemove = [NSMutableArray array];
for (id key in md) {
if ([key hasPrefix:@"tmp_"]) [keysToRemove addObject:key];
}
[md removeObjectsForKeys:keysToRemove];
// ✅ 或使用 keysSortedByValueUsingSelector: 取得鍵陣列再操作
| 操作 | 時間複雜度 | 說明 |
|---|---|---|
查找 objectForKey: | O(1) 平均 | 雜湊表實作,良好雜湊函數下常數時間 |
| 插入/刪除 | O(1) 攤還 | 負載因子超閾值觸發重新雜湊 (rehash) |
| 枚舉 | O(n) | 遍歷所有桶位 |
| 記憶體開銷 | ~2-3x 元素大小 | 雜湊表需保持低負載因子 (通常 < 0.75) |
@interface MyKey : NSObject
@property (copy) NSString *identifier;
@end
@implementation MyKey
- (BOOL)isEqual:(id)object {
return [object isKindOfClass:[MyKey class]] &&
[self.identifier isEqual:object.identifier];
}
- (NSUInteger)hash {
return [self.identifier hash];
}
- (id)copyWithZone:(NSZone *)zone {
return self; // 不可變物件可直接返回 self
}
@end
鐵律:作為字典鍵的物件必須實作 `
NSMutableDictionary *md = [NSMutableDictionary dictionary];
[md addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew context:nil];
// 修改 md 會觸發 observeValueForKeyPath:ofObject:change:context:
// 若鍵為大量字串,建立時預先計算 hash 可加速後續查找
// 但 NSString 已內建快取 hash,通常無需手動干預
| 陷阱 | 症狀 | 修正 |
|---|---|---|
| 以可變物件作鍵 | 修改鍵內容後查找失敗 | 鍵必須不可變,或複製一份不可變副本 |
存入 nil 值 | 編譯錯誤 / 執行期崩潰 | 用 [NSNull null] 代表空值 |
| 多執行緒同時讀寫 NSMutableDictionary | 資料竄改、崩潰 | 用 `@synchronized`、GCD serial queue、或改用不可變字典 + 原子交換 |
| 假設枚舉順序固定 | 邏輯錯誤 | 字典無序,勿依賴順序 |
// Swift 端
let nsDict: NSDictionary = ["name": "Swift", "version": 5]
let swiftDict = nsDict as? [String: Any] // 橋接為原生 Dictionary
// Objective-C 端
NSDictionary *dict = @{@"lang": @"ObjC"};
// Swift 可直接當作 [String: Any] 使用
`NSDictionary`/`NSMutableDictionary` 是 Foundation 資料處理的基石。掌握建立方式、CRUD 操作、鍵的協定要求、執行緒安全、效能特徵,能避開 90% 的字典相關坑。原則:**預設用不可變字典,確有修改需求再升級為可變;鍵永遠用不可變物件。**