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

掌握Objective-C循环语句(for、while、do-while)

2023-04-286.7k 阅读

Objective-C 循环语句概述

在编程的世界里,循环语句是实现重复执行特定代码块的关键工具。Objective-C 作为一门强大的面向对象编程语言,提供了三种主要的循环语句:for 循环、while 循环和 do - while 循环。这些循环语句在不同的场景下各有优劣,理解并熟练运用它们对于编写高效、简洁的代码至关重要。

循环语句的基本概念

循环语句的核心目的是让计算机重复执行一段代码,直到满足特定的条件为止。这个特定条件的判断决定了循环何时结束。在编程过程中,许多任务都具有重复性,比如遍历数组中的所有元素、对一系列数据进行相同的计算操作等,循环语句能够极大地简化这些任务的实现过程。

循环语句与程序控制流

循环语句是程序控制流的重要组成部分。程序的执行顺序通常是自上而下的,但循环语句打破了这种线性执行模式,使得程序能够根据条件反复执行特定的代码段。通过合理运用循环语句,可以实现复杂的算法和逻辑,让程序更加灵活和智能。

for 循环

for 循环的语法结构

for 循环在 Objective-C 中的语法结构如下:

for (初始化表达式; 条件表达式; 递增或递减表达式) {
    // 循环体代码
}
  1. 初始化表达式:这部分代码在循环开始前只执行一次,通常用于初始化循环控制变量。例如,我们可以在这里声明一个整型变量并赋予初始值。
  2. 条件表达式:在每次循环迭代开始时,都会计算这个条件表达式的值。如果该值为 true,则执行循环体中的代码;如果为 false,则循环结束,程序继续执行循环之后的代码。
  3. 递增或递减表达式:在每次循环体执行完毕后,会执行这部分代码,通常用于更新循环控制变量的值,使循环朝着结束的方向发展。

for 循环的执行流程

  1. 首先执行初始化表达式,初始化循环控制变量。
  2. 然后计算条件表达式的值。如果为 true,进入循环体执行代码;如果为 false,循环结束。
  3. 执行完循环体后,执行递增或递减表达式,更新循环控制变量。
  4. 再次计算条件表达式的值,重复步骤 2 到 4,直到条件表达式的值为 false

for 循环示例

  1. 简单的计数循环
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        for (int i = 0; i < 5; i++) {
            NSLog(@"当前计数: %d", i);
        }
    }
    return 0;
}

在这个例子中,我们使用 for 循环从 0 计数到 4。初始化表达式 int i = 0 声明并初始化了变量 i 为 0。条件表达式 i < 5 确保 i 的值小于 5 时循环继续执行。每次循环结束后,递增表达式 i++i 的值增加 1。

  1. 遍历数组
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *numbers = @[@1, @2, @3, @4, @5];
        for (NSUInteger i = 0; i < numbers.count; i++) {
            NSLog(@"数组元素: %@", numbers[i]);
        }
    }
    return 0;
}

这里我们创建了一个包含整数的 NSArrayfor 循环的初始化表达式 NSUInteger i = 0 初始化了索引变量 i。条件表达式 i < numbers.count 保证索引不超过数组的长度。每次循环,递增表达式 i++ 使索引值增加 1,从而遍历数组中的每个元素。

for 循环的嵌套

for 循环可以嵌套使用,即在一个 for 循环的循环体内再包含另一个 for 循环。这在处理二维数据结构(如二维数组)时非常有用。

示例代码如下:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                NSLog(@"(%d, %d)", i, j);
            }
        }
    }
    return 0;
}

在这个例子中,外层 for 循环控制行数,内层 for 循环控制列数。外层循环每执行一次,内层循环会完整执行一遍,从而输出一个 3x3 的坐标矩阵。

for 循环的高级用法

  1. 使用逗号运算符:在初始化表达式和递增或递减表达式中,可以使用逗号运算符来执行多个语句。
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 0, b = 0;
        for (a = 0, b = 10; a < b; a++, b--) {
            NSLog(@"a: %d, b: %d", a, b);
        }
    }
    return 0;
}

这里在初始化表达式中,我们同时初始化了 ab 两个变量,在递增或递减表达式中,同时更新了 ab 的值。

  1. 省略部分表达式for 循环的三个表达式都可以省略,但分号不能省略。如果省略条件表达式,需要在循环体内手动添加跳出循环的逻辑,否则会形成无限循环。
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int i = 0;
        for (; i < 5;) {
            NSLog(@"计数: %d", i);
            i++;
        }
    }
    return 0;
}

在这个例子中,我们将初始化表达式放在了 for 循环外部,省略了初始化表达式部分,递增表达式移到了循环体内,但条件表达式保留,这样的写法同样能实现正常的计数循环。

while 循环

while 循环的语法结构

while 循环的语法结构如下:

while (条件表达式) {
    // 循环体代码
}

while 循环只有一个条件表达式。在每次循环开始前,会计算这个条件表达式的值。如果值为 true,则执行循环体中的代码;如果为 false,则循环结束,程序继续执行循环之后的代码。

while 循环的执行流程

  1. 计算条件表达式的值。
  2. 如果条件表达式的值为 true,执行循环体代码。
  3. 循环体代码执行完毕后,再次计算条件表达式的值,重复步骤 2 和 3,直到条件表达式的值为 false

while 循环示例

  1. 简单的累加器
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int sum = 0;
        int num = 1;
        while (num <= 10) {
            sum += num;
            num++;
        }
        NSLog(@"1 到 10 的累加和: %d", sum);
    }
    return 0;
}

在这个例子中,我们使用 while 循环计算 1 到 10 的累加和。初始化变量 sum 为 0 用于存储累加结果,num 为 1 作为计数变量。条件表达式 num <= 10 确保 num 的值不超过 10 时循环继续。每次循环,将 num 的值加到 sum 中,并将 num 的值增加 1。

  1. 用户输入验证
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSInteger input;
        while (true) {
            NSLog(@"请输入一个 1 到 10 之间的整数:");
            scanf("%ld", &input);
            if (input >= 1 && input <= 10) {
                NSLog(@"你输入了一个有效的数字: %ld", input);
                break;
            } else {
                NSLog(@"输入无效,请重新输入。");
            }
        }
    }
    return 0;
}

这里我们使用了一个 while (true) 的无限循环,在循环体内,提示用户输入一个 1 到 10 之间的整数。如果输入的数字在有效范围内,输出提示信息并使用 break 语句跳出循环;如果输入无效,提示用户重新输入。

while 循环与 for 循环的比较

  1. 适用场景for 循环通常适用于已知循环次数的场景,比如遍历固定长度的数组。而 while 循环更适用于循环次数不确定,需要根据某个条件动态决定是否继续循环的场景,如上述的用户输入验证示例。
  2. 代码结构for 循环将初始化、条件判断和变量更新集中在一处,代码结构更加紧凑,可读性较好。while 循环则更侧重于条件判断,初始化和变量更新可能在循环外部或内部的不同位置,代码结构相对灵活。

do - while 循环

do - while 循环的语法结构

do - while 循环的语法结构如下:

do {
    // 循环体代码
} while (条件表达式);

do - while 循环与 while 循环类似,但有一个重要区别:do - while 循环的循环体至少会执行一次,因为条件表达式是在循环体执行完毕后才进行判断。

do - while 循环的执行流程

  1. 首先执行循环体代码。
  2. 循环体代码执行完毕后,计算条件表达式的值。
  3. 如果条件表达式的值为 true,再次执行循环体代码;如果为 false,循环结束,程序继续执行循环之后的代码。

do - while 循环示例

  1. 简单的菜单选择
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSInteger choice;
        do {
            NSLog(@"请选择操作: 1. 加法 2. 减法 3. 退出");
            scanf("%ld", &choice);
            switch (choice) {
                case 1:
                    NSLog(@"执行加法操作");
                    break;
                case 2:
                    NSLog(@"执行减法操作");
                    break;
                case 3:
                    NSLog(@"退出程序");
                    break;
                default:
                    NSLog(@"无效选择,请重新输入");
                    break;
            }
        } while (choice != 3);
    }
    return 0;
}

在这个例子中,我们使用 do - while 循环实现了一个简单的菜单选择功能。无论用户第一次输入什么,菜单都会至少显示一次。用户选择操作后,程序根据选择执行相应的逻辑。如果用户选择 3(退出),则条件表达式 choice != 3false,循环结束,程序退出。

  1. 读取文件内容
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FILE *file = fopen("test.txt", "r");
        if (file != NULL) {
            char buffer[100];
            do {
                char *result = fgets(buffer, sizeof(buffer), file);
                if (result != NULL) {
                    NSLog(@"%s", buffer);
                }
            } while (!feof(file));
            fclose(file);
        } else {
            NSLog(@"无法打开文件");
        }
    }
    return 0;
}

这里我们使用 do - while 循环读取文件 test.txt 的内容。每次循环尝试读取一行内容到 buffer 中,如果读取成功则输出。条件表达式 !feof(file) 确保在文件未到达末尾时继续循环。由于 do - while 循环的特性,即使文件为空,循环体也会执行一次读取操作,然后根据结果判断是否继续循环。

do - while 循环与 while 循环的比较

  1. 执行次数while 循环可能一次都不执行循环体,取决于条件表达式的初始值。而 do - while 循环无论条件表达式的值如何,循环体至少会执行一次。
  2. 应用场景:当需要先执行一次操作,然后根据操作结果决定是否继续循环时,do - while 循环是更好的选择,如上述的菜单选择和文件读取示例。而 while 循环更适用于在执行操作前需要先判断条件的场景。

循环语句中的控制语句

break 语句

break 语句用于立即终止当前循环,跳出循环体。程序将继续执行循环之后的代码。在嵌套循环中,break 语句只会终止最内层的循环。

示例代码如下:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                break;
            }
            NSLog(@"当前计数: %d", i);
        }
        NSLog(@"循环结束后执行的代码");
    }
    return 0;
}

在这个例子中,当 i 的值等于 5 时,break 语句被执行,循环立即终止,程序继续执行 NSLog(@"循环结束后执行的代码"); 这行代码。

continue 语句

continue 语句用于跳过当前循环体中剩余的代码,直接进入下一次循环的条件判断。在嵌套循环中,continue 语句只影响当前所在的循环层。

示例代码如下:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0) {
                continue;
            }
            NSLog(@"当前奇数: %d", i);
        }
    }
    return 0;
}

在这个例子中,当 i 是偶数时,continue 语句被执行,跳过 NSLog(@"当前奇数: %d", i); 这行代码,直接进入下一次循环的条件判断,因此只有奇数会被输出。

return 语句

return 语句不仅用于从函数中返回值,在循环语句中也可以使用。当在循环体内执行 return 语句时,会立即终止当前函数的执行,包括循环,返回到调用该函数的地方。

示例代码如下:

#import <Foundation/Foundation.h>

int sumToLimit(int limit) {
    int sum = 0;
    for (int i = 1; i <= limit; i++) {
        if (sum + i > 100) {
            return sum;
        }
        sum += i;
    }
    return sum;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int result = sumToLimit(20);
        NSLog(@"累加结果: %d", result);
    }
    return 0;
}

sumToLimit 函数中,当累加和 sum 加上当前的 i 值超过 100 时,执行 return sum; 语句,不仅终止了 for 循环,还从函数中返回了当前的累加和。

循环语句的性能考虑

不同循环语句的性能差异

在大多数情况下,forwhiledo - while 循环在性能上并没有显著的差异。现代编译器会对这些循环进行优化,使得它们的执行效率相近。然而,在一些极端情况下,细微的性能差异可能会显现出来。

例如,在循环次数固定且较少的情况下,for 循环由于其紧凑的代码结构,编译器可能更容易进行优化,从而在性能上略胜一筹。而对于循环次数不确定且依赖复杂条件判断的情况,whiledo - while 循环的灵活性可能更重要,性能上的差异则可以忽略不计。

优化循环性能的方法

  1. 减少循环体内的开销:尽量避免在循环体内执行复杂的计算或函数调用,尤其是那些不依赖循环变量的操作。可以将这些操作移到循环外部。
// 未优化的代码
for (int i = 0; i < 1000; i++) {
    double result = sin(M_PI / 4) * i;
    NSLog(@"结果: %f", result);
}

// 优化后的代码
double factor = sin(M_PI / 4);
for (int i = 0; i < 1000; i++) {
    double result = factor * i;
    NSLog(@"结果: %f", result);
}

在这个例子中,sin(M_PI / 4) 的计算不依赖于循环变量 i,将其移到循环外部,减少了循环体内的计算开销。

  1. 避免不必要的内存分配:在循环体内频繁分配和释放内存会导致性能下降。尽量提前分配足够的内存空间,或者使用对象池等技术来复用对象。
// 未优化的代码
for (int i = 0; i < 1000; i++) {
    NSMutableArray *array = [NSMutableArray array];
    // 对 array 进行操作
    // 循环结束后 array 被释放
}

// 优化后的代码
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 1000; i++) {
    // 对 array 进行操作
}

这里提前创建了 NSMutableArray 对象,避免了在循环体内频繁创建和销毁对象。

  1. 使用合适的循环类型:根据实际需求选择合适的循环类型。如果循环次数已知,优先使用 for 循环;如果需要根据条件动态决定循环次数且可能一次都不执行循环体,使用 while 循环;如果循环体至少要执行一次,使用 do - while 循环。

总结循环语句的使用要点

  1. 理解语法和执行流程:深入理解 forwhiledo - while 循环的语法结构和执行流程是正确使用它们的基础。掌握每个循环的特点,才能在不同场景下选择最合适的循环类型。
  2. 注意循环控制变量:合理初始化、更新和判断循环控制变量,确保循环能够正常结束,避免出现无限循环的情况。在嵌套循环中,要特别注意不同层次循环控制变量之间的关系。
  3. 结合控制语句:善于使用 breakcontinuereturn 等控制语句来灵活控制循环的执行流程,实现复杂的逻辑。但要注意这些控制语句的使用范围和对程序结构的影响。
  4. 性能优化:在编写循环代码时,要考虑性能因素,尽量减少循环体内的开销,避免不必要的内存分配,以提高程序的执行效率。

通过熟练掌握 Objective-C 的循环语句,你将能够编写出更加高效、灵活和健壮的程序,无论是开发 iOS 应用、Mac 应用还是其他基于 Objective-C 的项目,循环语句都将是你编程过程中的得力工具。在实际编程中,不断练习和总结经验,进一步提升对循环语句的运用能力。