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

Objective-C数组的创建、访问与操作技巧

2022-11-202.0k 阅读

1. Objective - C数组概述

在Objective - C编程中,数组是一种非常重要的数据结构,用于存储和管理一组相同类型的数据。数组在内存中是连续存储的,这使得对数组元素的访问效率较高。Objective - C提供了两种主要类型的数组:C 风格数组和Foundation框架中的NSArray及其可变子类NSMutableArray。C风格数组是基于C语言的,是一种静态数组,其大小在编译时就必须确定;而NSArray和NSMutableArray是Objective - C面向对象的数组,大小可以动态变化,并且提供了丰富的方法来操作数组元素。

2. C风格数组的创建

2.1 基本创建方式

C风格数组的声明语法为:数据类型 数组名[数组大小];。例如,创建一个存储整数的数组:

int intArray[5];

这里创建了一个名为intArray的数组,它可以存储5个int类型的元素。数组的下标从0开始,所以有效的下标范围是0到4。

如果在创建数组时初始化数组元素,可以这样做:

int intArray[5] = {1, 2, 3, 4, 5};

也可以省略数组大小,让编译器根据初始化列表来推断数组的大小:

int intArray[] = {1, 2, 3, 4, 5};

2.2 创建多维数组

多维数组本质上是数组的数组。例如,创建一个二维数组:

int twoDArray[3][4];

这创建了一个3行4列的二维数组。初始化二维数组可以如下:

int twoDArray[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

3. C风格数组的访问

3.1 访问单个元素

访问C风格数组的元素通过下标操作符[]。例如,访问前面创建的intArray数组的第三个元素:

int value = intArray[2];

需要注意的是,下标从0开始,所以intArray[2]实际上是数组中的第三个元素。

3.2 遍历数组

遍历数组通常使用循环。对于一维数组,可以使用for循环:

for (int i = 0; i < 5; i++) {
    NSLog(@"%d", intArray[i]);
}

对于二维数组,需要使用嵌套的for循环:

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        NSLog(@"%d", twoDArray[i][j]);
    }
}

4. C风格数组的操作技巧

4.1 数组作为函数参数

可以将数组作为函数的参数传递。例如:

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        NSLog(@"%d", arr[i]);
    }
}

调用这个函数时:

int intArray[] = {1, 2, 3, 4, 5};
printArray(intArray, 5);

需要注意的是,在函数中,数组参数会退化为指针,所以无法通过sizeof(arr)获取数组的实际大小,需要额外传递数组大小参数。

4.2 动态分配数组内存

使用malloc函数可以动态分配数组内存,这样数组的大小可以在运行时确定。例如:

int size = 10;
int *dynamicArray = (int *)malloc(size * sizeof(int));
if (dynamicArray != NULL) {
    for (int i = 0; i < size; i++) {
        dynamicArray[i] = i * 2;
    }
    for (int i = 0; i < size; i++) {
        NSLog(@"%d", dynamicArray[i]);
    }
    free(dynamicArray);
}

这里使用malloc分配了一个能存储10个int类型元素的数组,使用完后需要通过free释放内存,以避免内存泄漏。

5. NSArray的创建

5.1 使用数组字面量创建

在Objective - C中,可以使用数组字面量非常简洁地创建NSArray。例如:

NSArray *array = @[@"Apple", @"Banana", @"Cherry"];

这里创建了一个包含三个字符串对象的NSArray。数组字面量是在iOS 4.0及以后引入的,大大简化了数组创建的代码。

5.2 使用工厂方法创建

NSArray提供了一些工厂方法来创建数组。例如,使用arrayWithObjects:方法:

NSString *str1 = @"Apple";
NSString *str2 = @"Banana";
NSString *str3 = @"Cherry";
NSArray *array = [NSArray arrayWithObjects:str1, str2, str3, nil];

注意,arrayWithObjects:方法需要以nil作为参数列表的结束标志,以表示数组元素的结束。

还可以使用arrayWithArray:方法从另一个数组创建新数组:

NSArray *originalArray = @[@"Apple", @"Banana", @"Cherry"];
NSArray *newArray = [NSArray arrayWithArray:originalArray];

6. NSArray的访问

6.1 通过索引访问元素

NSArray的元素可以通过objectAtIndex:方法访问。例如:

NSArray *array = @[@"Apple", @"Banana", @"Cherry"];
NSString *fruit = [array objectAtIndex:1];
NSLog(@"%@", fruit);

这里通过objectAtIndex:1获取数组中的第二个元素(下标从0开始)。

6.2 遍历NSArray

NSArray可以使用for - in循环进行遍历:

NSArray *array = @[@"Apple", @"Banana", @"Cherry"];
for (NSString *fruit in array) {
    NSLog(@"%@", fruit);
}

也可以使用传统的for循环结合count属性:

NSArray *array = @[@"Apple", @"Banana", @"Cherry"];
NSUInteger count = [array count];
for (NSUInteger i = 0; i < count; i++) {
    NSString *fruit = [array objectAtIndex:i];
    NSLog(@"%@", fruit);
}

7. NSArray的操作技巧

7.1 获取数组元素数量

通过count属性可以获取NSArray中元素的数量。例如:

NSArray *array = @[@"Apple", @"Banana", @"Cherry"];
NSUInteger count = [array count];
NSLog(@"数组元素数量: %lu", (unsigned long)count);

7.2 检查数组是否包含某个对象

可以使用containsObject:方法检查NSArray是否包含某个特定的对象。例如:

NSArray *array = @[@"Apple", @"Banana", @"Cherry"];
BOOL contains = [array containsObject:@"Banana"];
if (contains) {
    NSLog(@"数组包含Banana");
} else {
    NSLog(@"数组不包含Banana");
}

7.3 数组的排序

NSArray提供了几种排序方法。例如,使用sortedArrayUsingSelector:方法对数组进行排序:

NSArray *unsortedArray = @[@"Banana", @"Apple", @"Cherry"];
NSArray *sortedArray = [unsortedArray sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"%@", sortedArray);

这里使用compare:方法对字符串数组进行了按字母顺序的排序。如果是自定义对象的数组,需要在自定义类中实现相应的比较方法。

8. NSMutableArray的创建

8.1 使用工厂方法创建

NSMutableArray是NSArray的可变子类,可以使用array方法创建一个空的可变数组:

NSMutableArray *mutableArray = [NSMutableArray array];

也可以使用arrayWithCapacity:方法创建一个指定初始容量的可变数组:

NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:10];

8.2 从NSArray转换创建

可以通过mutableCopy方法从一个NSArray创建一个NSMutableArray。例如:

NSArray *originalArray = @[@"Apple", @"Banana", @"Cherry"];
NSMutableArray *mutableArray = [originalArray mutableCopy];

9. NSMutableArray的访问

NSMutableArray继承自NSArray,所以访问元素的方式与NSArray相同,即通过objectAtIndex:方法和for - in循环等方式。例如:

NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"Apple", @"Banana", @"Cherry", nil];
NSString *fruit = [mutableArray objectAtIndex:1];
NSLog(@"%@", fruit);

10. NSMutableArray的操作技巧

10.1 添加元素

NSMutableArray提供了多种添加元素的方法。例如,使用addObject:方法添加单个元素:

NSMutableArray *mutableArray = [NSMutableArray array];
[mutableArray addObject:@"Apple"];

使用addObjectsFromArray:方法可以从另一个数组添加多个元素:

NSArray *arrayToAdd = @[@"Banana", @"Cherry"];
[mutableArray addObjectsFromArray:arrayToAdd];

10.2 删除元素

可以使用removeObject:方法删除数组中的某个对象:

NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"Apple", @"Banana", @"Cherry", nil];
[mutableArray removeObject:@"Banana"];

使用removeObjectAtIndex:方法可以根据下标删除元素:

[mutableArray removeObjectAtIndex:0];

10.3 替换元素

使用replaceObjectAtIndex:withObject:方法可以替换指定下标的元素。例如:

NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"Apple", @"Banana", @"Cherry", nil];
[mutableArray replaceObjectAtIndex:1 withObject:@"Orange"];

10.4 数组的拼接

可以通过addObjectsFromArray:方法将一个数组的元素添加到另一个可变数组中,实现数组的拼接。例如:

NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"Apple", @"Banana", nil];
NSArray *array2 = @[@"Cherry", @"Date"];
[array1 addObjectsFromArray:array2];

10.5 数组的裁剪

可以使用removeLastObject方法删除数组的最后一个元素,实现类似裁剪的效果。例如:

NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"Apple", @"Banana", @"Cherry", nil];
[mutableArray removeLastObject];

11. 数组的内存管理

11.1 C风格数组的内存管理

C风格数组如果是在栈上创建(如int intArray[5];),则在其作用域结束时会自动释放内存。如果是通过malloc动态分配的内存(如int *dynamicArray = (int *)malloc(size * sizeof(int));),则需要使用free函数手动释放内存,以避免内存泄漏。

11.2 NSArray和NSMutableArray的内存管理

在ARC(自动引用计数)环境下,NSArray和NSMutableArray的内存管理相对简单。当数组对象不再被引用时,ARC会自动释放相关内存。例如,当一个包含对象的数组被释放时,数组中包含的对象如果没有其他强引用,也会被自动释放。

在MRC(手动引用计数)环境下,需要手动管理数组及其包含对象的引用计数。当向数组中添加对象时,对象的引用计数会增加;当从数组中移除对象或数组本身被释放时,需要确保相应对象的引用计数被正确减少,以避免内存泄漏或悬空指针。例如,在MRC下创建一个数组并添加对象:

NSString *str = [[NSString alloc] initWithString:@"Apple"];
NSMutableArray *mutableArray = [NSMutableArray array];
[mutableArray addObject:str];
[str release];

这里手动释放了str对象的引用,因为addObject:方法会增加对象的引用计数。当从数组中移除对象时:

[mutableArray removeObjectAtIndex:0];
// 这里对象的引用计数会减少,如果没有其他引用,对象会被释放

12. 数组与其他数据结构的结合使用

12.1 数组与字典

可以将数组作为字典的值,或者将字典作为数组的元素。例如,创建一个字典,其值为数组:

NSArray *fruits = @[@"Apple", @"Banana", @"Cherry"];
NSDictionary *dictionary = @{@"Fruits": fruits};

也可以创建一个数组,其元素为字典:

NSDictionary *dict1 = @{@"Name": @"Apple", @"Color": @"Red"};
NSDictionary *dict2 = @{@"Name": @"Banana", @"Color": @"Yellow"};
NSArray *arrayOfDicts = @[dict1, dict2];

12.2 数组与集合

NSSet是一种无序的集合,不允许有重复元素。可以将数组转换为集合,或者从集合创建数组。例如,将数组转换为集合:

NSArray *array = @[@"Apple", @"Banana", @"Apple"];
NSSet *set = [NSSet setWithArray:array];

这里集合set将只包含两个元素,因为重复的Apple被去除了。从集合创建数组:

NSArray *newArray = [set allObjects];

13. 数组在实际应用中的场景

13.1 数据展示

在iOS应用开发中,经常使用数组来存储要展示的数据,如表格视图(UITableView)或集合视图(UICollectionView)的数据。例如,一个新闻应用可能使用数组存储新闻标题和内容,然后通过表格视图展示给用户。

NSArray *newsTitles = @[@"新闻1", @"新闻2", @"新闻3"];
NSArray *newsContents = @[@"新闻内容1", @"新闻内容2", @"新闻内容3"];
// 在UITableView的数据源方法中使用这些数组来填充表格
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return newsTitles.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.textLabel.text = newsTitles[indexPath.row];
    return cell;
}

13.2 算法实现

在算法实现中,数组常常作为基本的数据结构。例如,排序算法(如冒泡排序、快速排序等)通常是在数组上进行操作的。以冒泡排序为例,对一个整数数组进行排序:

NSMutableArray *array = [NSMutableArray arrayWithArray:@[@3, @1, @4, @1, @5, @9]];
NSUInteger count = array.count;
for (NSUInteger i = 0; i < count - 1; i++) {
    for (NSUInteger j = 0; j < count - i - 1; j++) {
        NSNumber *num1 = array[j];
        NSNumber *num2 = array[j + 1];
        if ([num1 integerValue] > [num2 integerValue]) {
            [array exchangeObjectAtIndex:j withObjectAtIndex:j + 1];
        }
    }
}
NSLog(@"%@", array);

13.3 游戏开发

在游戏开发中,数组可以用于存储游戏对象的位置、状态等信息。例如,一个简单的2D游戏中,可能使用二维数组来表示游戏地图,每个元素表示地图上的一个位置,存储该位置的地形信息或是否有障碍物等。

// 假设0表示空地,1表示障碍物
NSMutableArray *gameMap = [NSMutableArray array];
for (NSUInteger i = 0; i < 10; i++) {
    NSMutableArray *row = [NSMutableArray array];
    for (NSUInteger j = 0; j < 10; j++) {
        NSNumber *value = (i % 2 == 0 && j % 2 == 0)? @1 : @0;
        [row addObject:value];
    }
    [gameMap addObject:row];
}
// 遍历地图并输出
for (NSUInteger i = 0; i < gameMap.count; i++) {
    NSMutableArray *row = gameMap[i];
    for (NSUInteger j = 0; j < row.count; j++) {
        NSNumber *value = row[j];
        NSLog(@"%@ ", value);
    }
    NSLog(@"\n");
}

通过以上对Objective - C数组的创建、访问和操作技巧的详细介绍,希望开发者能够更加熟练地运用数组这种重要的数据结构,开发出更加高效、稳定的应用程序。无论是在iOS应用开发、算法实现还是其他领域,数组都发挥着不可或缺的作用。在实际编程中,需要根据具体的需求选择合适的数组类型(C风格数组、NSArray或NSMutableArray),并合理运用其特性和操作方法,以达到最佳的编程效果。同时,要注意内存管理,特别是在MRC环境下,避免内存泄漏等问题。