MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C 集合框架(NSArray、NSDictionary 等)详解

2023-12-087.0k 阅读

Objective-C 集合框架概述

在 Objective-C 编程中,集合框架提供了强大的数据存储和管理功能。集合框架主要包括 NSArray、NSMutableArray、NSDictionary、NSMutableDictionary 以及 NSSet、NSMutableSet 等类。这些类让开发者能够高效地处理一组数据,无论是简单的有序列表,还是复杂的键值对集合。

NSArray

1. 不可变数组的定义与初始化

NSArray 是一个不可变数组类,一旦创建,其内容就不能被修改。定义和初始化 NSArray 有多种方式。

  • 使用 @[] 字面量语法
    NSArray *array1 = @[@"Apple", @"Banana", @"Cherry"];
    
    这种方式简洁明了,是现代 Objective-C 中常用的初始化数组的方法。
  • 使用 arrayWithObjects: 方法
    NSArray *array2 = [NSArray arrayWithObjects:@"Dog", @"Cat", @"Bird", nil];
    
    注意,使用 arrayWithObjects: 方法时,必须以 nil 作为参数列表的结束标志。

2. 访问数组元素

NSArray 的元素可以通过索引进行访问,索引从 0 开始。

NSArray *fruits = @[@"Apple", @"Banana", @"Cherry"];
NSString *firstFruit = fruits[0];
NSString *secondFruit = [fruits objectAtIndex:1];

这里,fruits[0][fruits objectAtIndex:0] 两种方式都可以获取数组的第一个元素。

3. 数组的属性与方法

  • count 属性:用于获取数组中元素的个数。
    NSArray *numbers = @[@1, @2, @3, @4, @5];
    NSUInteger count = numbers.count;
    NSLog(@"数组元素个数: %lu", (unsigned long)count);
    
  • containsObject: 方法:判断数组是否包含某个特定的对象。
    NSArray *colors = @[@"Red", @"Green", @"Blue"];
    BOOL containsGreen = [colors containsObject:@"Green"];
    if (containsGreen) {
        NSLog(@"数组包含 Green");
    } else {
        NSLog(@"数组不包含 Green");
    }
    
  • indexOfObject: 方法:获取某个对象在数组中的索引,如果对象不存在则返回 NSNotFound。
    NSArray *animals = @[@"Lion", @"Tiger", @"Bear"];
    NSUInteger index = [animals indexOfObject:@"Tiger"];
    if (index != NSNotFound) {
        NSLog(@"Tiger 的索引是: %lu", (unsigned long)index);
    } else {
        NSLog(@"Tiger 不在数组中");
    }
    

NSMutableArray

1. 可变数组的定义与初始化

NSMutableArray 继承自 NSArray,它允许在创建后添加、删除和修改数组中的元素。

  • 使用 @[] 字面量语法结合 mutableCopy 方法
    NSArray *immutableArray = @[@"One", @"Two", @"Three"];
    NSMutableArray *mutableArray1 = [immutableArray mutableCopy];
    
  • 直接使用 NSMutableArray 的初始化方法
    NSMutableArray *mutableArray2 = [NSMutableArray arrayWithObjects:@"Four", @"Five", nil];
    

2. 添加元素

  • addObject: 方法:向数组末尾添加一个对象。
    NSMutableArray *shoppingList = [NSMutableArray arrayWithObject:@"Milk"];
    [shoppingList addObject:@"Bread"];
    
  • insertObject:atIndex: 方法:在指定索引位置插入一个对象。
    NSMutableArray *numbers = [NSMutableArray arrayWithArray:@[@1, @3, @4]];
    [numbers insertObject:@2 atIndex:1];
    

3. 删除元素

  • removeObject: 方法:从数组中删除指定的对象。
    NSMutableArray *colors = [NSMutableArray arrayWithArray:@[@"Red", @"Green", @"Blue"]];
    [colors removeObject:@"Green"];
    
  • removeObjectAtIndex: 方法:删除指定索引位置的对象。
    NSMutableArray *fruits = [NSMutableArray arrayWithArray:@[@"Apple", @"Banana", @"Cherry"]];
    [fruits removeObjectAtIndex:1];
    
  • removeAllObjects 方法:清空数组中的所有元素。
    NSMutableArray *array = [NSMutableArray arrayWithArray:@[@"A", @"B", @"C"]];
    [array removeAllObjects];
    

4. 修改元素

可以直接通过索引来修改可变数组中的元素。

NSMutableArray *words = [NSMutableArray arrayWithArray:@[@"Hello", @"World"]];
words[1] = @"Universe";

NSDictionary

1. 不可变字典的定义与初始化

NSDictionary 是一个不可变的键值对集合,每个键必须是唯一的。

  • 使用 @{} 字面量语法
    NSDictionary *person = @{@"name": @"John", @"age": @30, @"city": @"New York"};
    
  • 使用 dictionaryWithObjectsAndKeys: 方法
    NSDictionary *book = [NSDictionary dictionaryWithObjectsAndKeys:@"Objective-C Programming", @"title", @"Author Name", @"author", nil];
    
    同样,dictionaryWithObjectsAndKeys: 方法需要以 nil 结束参数列表,并且对象在前,键在后。

2. 访问字典值

可以通过键来访问字典中的值。

NSDictionary *car = @{@"brand": @"Toyota", @"model": @"Corolla", @"year": @2023};
NSString *brand = car[@"brand"];
NSNumber *year = [car objectForKey:@"year"];

这里,car[@"brand"][car objectForKey:@"brand"] 两种方式都能获取对应键的值。

3. 字典的属性与方法

  • count 属性:获取字典中键值对的数量。
    NSDictionary *countries = @{@"USA": @"Washington, D.C.", @"China": @"Beijing", @"France": @"Paris"};
    NSUInteger count = countries.count;
    NSLog(@"字典中键值对数量: %lu", (unsigned long)count);
    
  • allKeys 方法:获取字典中所有的键。
    NSDictionary *languages = @{@"en": @"English", @"zh": @"Chinese", @"fr": @"French"};
    NSArray *keys = languages.allKeys;
    NSLog(@"所有键: %@", keys);
    
  • allValues 方法:获取字典中所有的值。
    NSArray *values = languages.allValues;
    NSLog(@"所有值: %@", values);
    

NSMutableDictionary

1. 可变字典的定义与初始化

NSMutableDictionary 继承自 NSDictionary,允许在创建后添加、删除和修改键值对。

  • 使用 @{} 字面量语法结合 mutableCopy 方法
    NSDictionary *immutableDict = @{@"key1": @"value1", @"key2": @"value2"};
    NSMutableDictionary *mutableDict1 = [immutableDict mutableCopy];
    
  • 直接使用 NSMutableDictionary 的初始化方法
    NSMutableDictionary *mutableDict2 = [NSMutableDictionary dictionaryWithObject:@"Initial Value" forKey:@"Initial Key"];
    

2. 添加键值对

  • setObject:forKey: 方法:添加一个新的键值对,如果键已存在,则更新对应的值。
    NSMutableDictionary *settings = [NSMutableDictionary dictionary];
    [settings setObject:@"Dark Mode" forKey:@"theme"];
    

3. 删除键值对

  • removeObjectForKey: 方法:删除指定键对应的键值对。
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{@"name": @"Alice", @"email": @"alice@example.com"}];
    [userInfo removeObjectForKey:@"email"];
    
  • removeAllObjects 方法:清空字典中的所有键值对。
    NSMutableDictionary *data = [NSMutableDictionary dictionaryWithDictionary:@{@"a": @1, @"b": @2, @"c": @3}];
    [data removeAllObjects];
    

4. 修改键值对

可以直接通过键来修改可变字典中的值。

NSMutableDictionary *config = [NSMutableDictionary dictionaryWithDictionary:@{@"server": @"localhost", @"port": @8080}];
config[@"port"] = @8081;

NSSet

1. 不可变集合的定义与初始化

NSSet 是一个不可变的无序集合,集合中的元素是唯一的。

  • 使用 @{} 字面量语法
    NSSet *set1 = @[@"Apple", @"Banana", @"Cherry"];
    
  • 使用 setWithObjects: 方法
    NSSet *set2 = [NSSet setWithObjects:@"Dog", @"Cat", @"Bird", nil];
    

2. 集合的属性与方法

  • count 属性:获取集合中元素的个数。
    NSSet *numbers = [NSSet setWithObjects:@1, @2, @3, nil];
    NSUInteger count = numbers.count;
    NSLog(@"集合元素个数: %lu", (unsigned long)count);
    
  • containsObject: 方法:判断集合是否包含某个特定的对象。
    NSSet *colors = [NSSet setWithObjects:@"Red", @"Green", @"Blue", nil];
    BOOL containsGreen = [colors containsObject:@"Green"];
    if (containsGreen) {
        NSLog(@"集合包含 Green");
    } else {
        NSLog(@"集合不包含 Green");
    }
    

NSMutableSet

1. 可变集合的定义与初始化

NSMutableSet 继承自 NSSet,允许在创建后添加和删除元素。

  • 使用 set 初始化方法
    NSMutableSet *mutableSet = [NSMutableSet set];
    
  • 使用 setWithSet: 方法从现有集合初始化
    NSSet *immutableSet = [NSSet setWithObjects:@"One", @"Two", nil];
    NSMutableSet *mutableSet2 = [NSMutableSet setWithSet:immutableSet];
    

2. 添加元素

  • addObject: 方法:向集合中添加一个对象,如果对象已存在,则不会重复添加。
    NSMutableSet *shoppingSet = [NSMutableSet setWithObject:@"Milk"];
    [shoppingSet addObject:@"Bread"];
    

3. 删除元素

  • removeObject: 方法:从集合中删除指定的对象。
    NSMutableSet *fruitSet = [NSMutableSet setWithObjects:@"Apple", @"Banana", @"Cherry", nil];
    [fruitSet removeObject:@"Banana"];
    

集合框架的内存管理

在 Objective-C 中,集合框架中的对象通常遵循引用计数的内存管理机制。当一个对象被添加到集合中时,集合会对该对象进行一次 retain(在 ARC 环境下,相当于增加引用计数)。当对象从集合中移除或者集合本身被释放时,集合会对对象进行一次 release(在 ARC 环境下,相当于减少引用计数)。

例如,在手动引用计数(MRC)环境下:

NSString *string = [[NSString alloc] initWithString:@"Example"];
NSMutableArray *array = [NSMutableArray array];
[array addObject:string];
[string release]; // 此时 string 的引用计数为 1,因为 array 持有它
[array removeObjectAtIndex:0]; // 此时 string 的引用计数变为 0,会被释放

在自动引用计数(ARC)环境下,开发者无需手动管理对象的引用计数,编译器会自动处理。

集合框架的性能考量

不同的集合类型在性能上有各自的特点。

  • NSArray:对于按顺序访问元素的场景,NSArray 表现出色,因为可以通过索引快速定位元素。查找特定元素时,如果元素数量较多,indexOfObject: 方法的时间复杂度为 O(n),性能相对较低。
  • NSDictionary:NSDictionary 适用于通过键快速查找值的场景,其查找操作的平均时间复杂度为 O(1)。但如果键的数量非常大,哈希冲突可能会影响性能。
  • NSSet:NSSet 适合判断元素是否存在的场景,其 containsObject: 方法平均时间复杂度为 O(1)。由于集合是无序的,不适合需要顺序访问元素的场景。

集合框架的遍历

1. NSArray 的遍历

  • 使用 for 循环
    NSArray *fruits = @[@"Apple", @"Banana", @"Cherry"];
    for (NSUInteger i = 0; i < fruits.count; i++) {
        NSString *fruit = fruits[i];
        NSLog(@"%@", fruit);
    }
    
  • 使用 fast enumeration
    for (NSString *fruit in fruits) {
        NSLog(@"%@", fruit);
    }
    
  • 使用 block 遍历
    [fruits enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@ at index %lu", obj, (unsigned long)idx);
        if (idx == 1) {
            *stop = YES;
        }
    }];
    

2. NSDictionary 的遍历

  • 使用 fast enumeration 遍历键
    NSDictionary *person = @{@"name": @"John", @"age": @30, @"city": @"New York"};
    for (NSString *key in person) {
        id value = person[key];
        NSLog(@"%@: %@", key, value);
    }
    
  • 使用 block 遍历键值对
    [person enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSLog(@"%@: %@", key, obj);
    }];
    

3. NSSet 的遍历

  • 使用 fast enumeration
    NSSet *colors = [NSSet setWithObjects:@"Red", @"Green", @"Blue", nil];
    for (NSString *color in colors) {
        NSLog(@"%@", color);
    }
    
  • 使用 block 遍历
    [colors enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSLog(@"%@", obj);
    }];
    

集合框架与其他数据类型的转换

1. NSArray 与 C 数组的转换

  • NSArray 转 C 数组
    NSArray *numbers = @[@1, @2, @3];
    NSUInteger count = numbers.count;
    int cArray[count];
    [numbers getObjects:(id *)cArray range:NSMakeRange(0, count)];
    for (NSUInteger i = 0; i < count; i++) {
        NSLog(@"%d", cArray[i]);
    }
    
  • C 数组转 NSArray
    int cArray2[] = {4, 5, 6};
    NSArray *arrayFromC = [NSArray arrayWithObjects:[NSNumber numberWithInt:cArray2[0]], [NSNumber numberWithInt:cArray2[1]], [NSNumber numberWithInt:cArray2[2]], nil];
    

2. NSDictionary 与 JSON 的转换

  • NSDictionary 转 JSON 数据
    NSDictionary *data = @{@"name": @"Alice", @"age": @25};
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    NSLog(@"%@", jsonString);
    
  • JSON 数据转 NSDictionary
    NSString *jsonString2 = @"{\"name\":\"Bob\",\"age\":30}";
    NSData *jsonData2 = [jsonString2 dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *dictFromJSON = [NSJSONSerialization JSONObjectWithData:jsonData2 options:NSJSONReadingMutableContainers error:nil];
    

通过对 Objective-C 集合框架中 NSArray、NSDictionary、NSSet 及其可变版本的详细介绍,开发者可以根据具体的需求选择合适的集合类型,高效地处理数据。同时,对集合框架的内存管理、性能考量、遍历方式以及与其他数据类型的转换等方面的理解,有助于编写出更加健壮和高效的 Objective-C 程序。