Objective-C & Sprite Kit太空历险记 : 3. 军官训练营——控制你的代码

原文

  http://www.ituring.com.cn/article/212223

   

前一章,在前哨基地里的初级训练已经完成了,接下来,我们会来到太空战舰“火神号”进行初级军官训练科目;在这里,C-S少校将负责大家的训练工作。

C-S少校:欢迎来到‘火神号’,本部分训练科目将会锻炼大家对工作的基本控制能力,以便胜任太空战舰各个岗位的工作。各位知道,我们经常需要根据不同的数据或条件执行相应的工作,而这些工作就是由控制语句来完成的;接下来,我们就来进行代码流程控制科目的训练,主要内容包括:

  • 比较运算
  • 逻辑运算
  • 条件语句
  • 选择语句
  • 循环语句

3.1. 比较运算

C-S少校:作为一名预备军官,你一定知道3比1大,所以,如果图案一样,当然是数量多的那一个军衔高。在代码中的实际操作,我们经常会对数据进行一些比较,比如,根据变量或常量的值来执行所需要的代码。

在Objective-C中,常用的比较运算包括:

  • 等于运算符==。
  • 不等于运算符!=。
  • 小于运算符<。
  • 小于等于运算符<=。
  • 大于运算符>。
  • 大于等于运算符>=。

C-S少校:作为军官,作出决定必须要果断,不要有大概、差不多之类的回答。所以,比较运算的结果会是BOOL类型,也就是说,你必须回答YES或NO。

在这里需要特别注意的等于运算符,它是两个等号,这和一个等号(赋值运算符)是不同的,而这是很多初学者最容易出错的地方之一。请注意分析以下几行代码的执行结果。

int intNum = 1;
NSLog(@"%i", intNum == 1);  //1, 变量intNum等于1
NSLog(@"%i", intNum == 0);  //0, 变量intNum不等于1
NSLog(@"%i", intNum = 1);  //1, 1赋值到intNum变量,表达式的值是intNum的值
NSLog(@"%i", intNum = 0);  //0, 表达式的值是intNum的值0

3.2. 逻辑运算

C-S少校:工作中,操作人员的逻辑判断能力同样重要。在代码中,使用逻辑运算(又称为布尔运算),可以帮助我们进行更复杂的条件判断。而且,对于逻辑运算,其结果同样要果断,即必须回答YES或NO。

在Objective-C中,逻辑运算符包括:

  • 逻辑与运算符&&。
  • 逻辑或运算符||。
  • 逻辑非运算符!。

逻辑与运算,使用两个运算数,只有两个运算数的值都是真(YES)时,运算结果才为真(YES),否则运算结果为假(NO)。

NSLog(@"%i", YES && YES);  // 1
NSLog(@"%i", YES && NO);  // 0
NSLog(@"%i", NO && NO);  // 0

逻辑或运算,使用两个运算数,当其中一个运算数的值为真(YES)时,运算结果就为真(YES),否则运算结果为假(NO)。如下面的代码。

NSLog(@"%i", YES || YES);  // 1
NSLog(@"%i", YES || NO);  // 1
NSLog(@"%i", NO || NO);  // 0

逻辑非运算,也称为取反运算,只需要一个运算数,当运算数为真(YES)时,取反运算结果为假(NO);当运算数为假(NO)时,取反运算结果为真(YES)。如下面的代码。

NSLog(@"%i", !YES);  // 0
NSLog(@"%i", !NO);  // 1

实际应用中,我们可以将多个比较运算和逻辑运算进行组合,从而执行更复杂的复合条件判断,在下面控制语句相关的训练科目中,我们可以看到具体的应用示例。

3.3. 条件语句

C-S少校:大家可以看到这种情况下,对于敌友的判断是非常重要的;此时,我们需要对来历不明的太空船进行身份判断,而在判断身份时,条件的设定是非常重要的,还是图中的情况,不是友机并不意味着要摧毁,但如果是敌机,则摧毁是必须的,所以,我们会快速地判断它是不是敌机,而不是判断是否为友机。在Objective-C代码中,对于条件的判断,可以有两种方法:

  • 标准方法,使用if语句结构。
  • 快捷方法,使用?:运算符。

3.3.1. if语句

if语句的基本应用格式如下:

if (<条件>) {
    <语句块>
}

当<条件>为成立(YES)时,就执行<语句块>,然后执行}后面的代码;如果<条件>不成立(NO)时,就直接执行}后面的代码。如下面的代码:

BOOL isEnemy = YES;
if (isEnemy) {
    NSLog(@"开火并摧毁");
}

如果不是敌机怎么办呢?我们可以添加else语句,如下面的代码。

BOOL isEnemy = NO;
if (isEnemy) {
    NSLog(@"开火并摧毁");
} else {
    NSLog(@"不是敌机,允许登舰");
}

更复杂的情况下,会有多个条件时,可以在结构中添加else if语句,如下面的代码:

if (<条件1>) {
    <语句块1>
}else if (<条件2>) {
    <语句块2>
}else if (<条件n>) {
    <语句块n>
}else {
    <语句块n+1>
}

在这种if语句结构中,else if可以有多个,也可以没有,而使用else语句时,只能放在所有条件的后面,而且只能有一个,即当所有条件都不满足时,会执行<语句块n+1>。

此外,对于条件的判断还可以嵌套使用,如下面的代码:

BOOL isEnemy = NO;
NSInteger tarmacNumber = 10;
if (isEnemy) {
    NSLog(@"开火并摧毁");
} else {
    if (tarmacNumber > 0){
        NSLog(@"请在 %li 号停机坪降落", tarmacNumber);
    } else {
        NSLog(@"请在公共停机坪降落");
    }
}

C-S少校:太空旅行中,时间的差异非常大,这是相对论所决定的,所以,对于标准时间的操作显得非常重要。比如,下面的代码,演示了如何使用复合条件判断一个年份是否为闰年。

int year = 2016;
if ((year % 100 != 0 && year % 4 == 0) || year % 400 == 0)
{
    NSLog(@"%i年是闰年", year);
} else {
    NSLog(@"%i年不是闰年", year);
}

如果大家在工作中需要更多关于日期的操作,请呼叫NSDate类型。看不明白战斗手册,也许完成第4章的内容后就好多了。

3.3.2. ?:运算符

C-S少校:战斗中,对于一些指令,需要简单明了,以提高指令传递的效率,要知道,一秒钟就可能决定战斗的成败。比如,还是在判断是否为敌机的问题上,我们可以使用?:运算符,它是唯一的一种三元运算符,需要三部分来参加运算,其格式如下。

<表达式1> ? <表达式2> : <表达式3>

其中,<表达式1>为逻辑表达式,其结果应该是BOOL类型,当其值为YES时,整个表达式的运算结果就是<表达式2>的值,如果为NO,整个表达式的运算结果就是<表达式3>的值。如下面的代码:

BOOL isEnemy = NO;
isEnemy ? NSLog(@"开火并摧毁") : NSLog(@"不是敌机,允许登舰");

C-S少校:如果表达式稍微复杂一点点,可以使用小括号()将三个表达式分别包装一下。

3.4. 选择语句

C-S少校:接下来的训练,我们会练习全地形战车的驾驶,和太空船的驾驶比较,战车会简单很多,毕竟只需要控制四个方向就差不多了。当然,这都太空时代了,一切都是代码来控制的,在这种情况下,我们会使用什么代码结构呢?和很多编程语言一样,在Objective-C中也可以使用switch语句结构,其应用格式如下:

switch(<表达式>)
{
case <值1>:
    {
        <语句块1>
    }break;
case <值2>:
    {
        <语句块2>
    }break;
case <值n>:
    {
        <语句块n>
    }break;
default:
    {
        <语句块n+1>
    }break;
}

在switch语句结构中,我们可以看到,只有一个确定执行条件的<表达式>,而每一个case语句对应一个<表达式>的值。在这里,应该注意的是每一个case所执行的语句块结束后,都应该有一个break语句,其原因是,case语句具有向下贯穿的特性,如果没有break语句中断当前case语句块,则会一直向下执行,直到遇到中断语句(如break、return等语句)或者是switch语句结构全部执行完成。

请注意,每个case语句或default语句后的代码也可以不使用花括号,这主要取决于编程习惯;而break语句也可以放在每个case后的{}中或在}后面,这也只是一个编程习惯问题。

下面的代码,将会模拟驾驶指令。

typedef enum EDirection {
Unknow, Up, Right, Down, Left
} Direction;
//
Direction d = Up;
switch (d) {
case Up:
    NSLog(@"前进");
    break;
case Right:
    NSLog(@"右转");
    break;
case Down:
    NSLog(@"倒车");
    break;
case Left:
    NSLog(@"左转");
    break;
default:
    NSLog(@"停止");
    break;
}

大家可以修改变量d的值,以观察代码执行的结果。

C-S少校:下面的代码,继续演示一个日期相关的处理,我们利用case的贯穿特性来返回某年某月中的天数。

int year = 2006;
int month = 2;
int days = 0;
switch(month)
{
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
    {
        days = 31;
    }break;
    case 4:
    case 6:
    case 9:
    case 11:
    {
        days = 30;
    }break;
    case 2:
    {
        days = (((year%100!=0 && year%4==0) || year%400==0) ? 29 : 28);
    }break;
}
NSLog(@"%i年%i月有%i天", year, month, days);

由于我们已经定义变量day的默认值是0,所以,在switch语句结构中就没有再使用default来设置没有对应值的处理了;但是,如果在代码中没有预设的默认情况,就需要考虑使用default语句,以保证<表达式>的结果是任何值时都会有相应的处理代码。

3.5. 循环语句

C-S少校:计算机最大的特点是什么?就是可以根据一定的条件自动完成工作,而且快速、高效、不易犯错;所以,在战舰里,有很多工作都是由计算机来自动完成的,而作为一名值班军官,你们的任务就是制定计划,并能够处理突发情况。

循环语句就是能够根据一定的条件重复执行一系列任务的语句结构。在Objective-C中,常用的循环语句包括for、while和do-while;此外,在循环语句中,还可以使用break和continue语句来控制循环语句的执行,下面,我们就相关科目的训练。

3.5.1. for语句结构

for语句的应用格式如下:

for (<表达式1>; <表达式2>; <表达式3>) {
    <语句块>
}

其中,<表达式1>用于定义循环控制变量,<表达式2>用于指定循环执行的条件,条件成员时(YES值)执行循环操作,条件不成立时(NO值)时终止循环;<表达式3>用于指定循环控制变量每次循环后的变化,而<语句块>就是每次循环所执行的代码。

如下面的代码,一号雷达将按顺序扫描1到10号区域。

for (int i=1; i <= 10; i++) {
    NSLog(@"一号雷达扫描%i号区域", i);
}

C-S少校:下面的代码是一个简单的数学问题,我们将计算1到100的和。

int sum = 0;
for (int i=1; i <= 100; i++) {
    sum += i;
}
NSLog(@"1100的和是%i", sum);

如果是计算1到100中偶数的和,可以修改for语句中的控制语句,如下面的代码。

for (int i=2; i <= 100; i+=2) 

3.5.2. while语句结构

C-S少校:前面,我们的一号雷达扫描一遍1到10区域就会停止工作,这是非常不安全的,它应该一遍又一遍地扫描这些区域,真到某种条件下停止工作;此时,我们可以考虑使用while或do-while语句结构,首先看一下while语句。

while语句的应用格式如下:

while (<条件>) {
    <语句块>
}

其中,当<条件>为真(YES)时,就会执行<语句块>,只有当<条件>为假(NO)时终止循环结构,继续执行}后面的代码。

下面的例子,一号雷达会不停的工作,大家不要急着在Xcode运行,因为这个代码不会停下来,直到程序挂掉为止。这种循环结构称为无限循环或死循环。

while (YES) {
    for (int i=1; i <= 10; i++) {
        NSLog(@"一号雷达扫描%i号区域", i);
    }
}

雷达扫描是一个特殊的工作,而在很多while应用中,都应该有一个合理的条件,以便while循环能够适时结束。如下面的代码,我们使用while语句完成1到100的累加工作。

int i = 1;
int sum = 0;
while (i <= 100) {
    sum += i;
    i++;
}
NSLog(@"1100的和是%i", sum);

3.5.3. do-while语句结构

C-S少校:do-while语句结构与while语句结构的使用比较相似,所不同的是,do-while语句结构会在每次循环结束后进行条件的判断,其基本应用格式如下:

do {
    <语句块>
}while (<条件>);

使用do-while语句结构时应注意,<语句块>最少会执行一次,不过,这也是可能出问题的地方,如果在<语句块>中存在不满足条件的代码,就有可能产生错误,所以,如果有可能,我们会更建议使用while语句结构。毕竟让太空船的控制计算机挂掉并不是一件好玩的事情。

3.6. 其他控制语句

C-S少校:本部分科目是一些特殊的控制语句,包括break、continue和goto语句。

3.6.1. break语句

C-S少校:循环语句的确可以简化很多工作,但是,我们也需要适时的终止循环操作;在Objective-C中,break语句就可以在循环结构中紧急刹车,从而终止整个循环的执行。如下面的代码,如果一号雷达发现可疑目标,就会显示信息,并锁定当前区域。

int lockedZone = 0;
for (int i=1; i <= 10; i++) {
    // 可疑情况的出现很随机
    if (arc4random() % 2 == 1) {
        NSLog(@"警告:一号雷达在%i号区域发现可疑目标", i);
        lockedZone = i;
        break;
    }
}

C-S少校:代码中,arc4random()函数的功能是产生一个随机整数,我们取这个随机数除以2的余数,余数为1时作为发现目标的条件;情况总是很随机的出现,所以,大家要时刻做好准备。

3.6.2. continue语句

C-S少校:continue语句的功能就是中止当前循环,并开始下一次循环(如果条件满足的话)。前面,一号雷达已经锁定lockedZone变量所指的区域,此时,二号雷达将代替一号雷达继续搜索其它区域,要知道,最先出现的目标很可能只是诱饵。

for (int i=1; i <= 10; i++) {
    if (lockedZone == i) continue;
    NSLog(@"二号雷达正在搜索%i号区域", i);
}

执行此代码,我们可以看到,二号雷达在到达lockedZone区域时不会执行扫描工作,因为一号雷达已经锁定此区域,此时,使用continue语句终止此次循环,并继续下一循环。

3.6.3. goto语句与标签

C-S少校:大家也许知道在C中关于goto语句的争议是很大的,不过,在一些特殊情况下,goto语句和标签的使用却是效率最高的,比如,在嵌套的循环语句中。如下面的代码。

int lockedZone = 0;
while (YES) {
    for (int i=1; i <= 10; i++) {
        // 可疑情况的出现很随机
        if (arc4random() % 2 == 1) {
            NSLog(@"警告:一号雷达在%i号区域发现可疑目标", i);
            lockedZone = i;
            goto RadarOneStop;
        }
    }
}
RadarOneStop:
NSLog(@"一号雷达锁定目标,停止扫描");

在这个代码中,当一号雷达发现了可疑目标以后,则锁定此区域,并停止工作,当然,在前面的示例中,我们也可以看到,实际会有二号雷达来继续扫描其他区域。

在Objective-C中,我们通过“<标签名称>+:”的格式来定义一个标签,然后,可以使用“goto <标签名称>;”语句跳转到此标签的位置。

C-S少校:各位,本部分的科目,我们讨论了在Obejctive-C中常用的一些逻辑运算,以及流程控制语句结构,利用这些内容,我们可以灵活地控制代码执行的逻辑,并创建出功能更加强大的代码。这些内容将是你们作为军官远征宇宙的基本技能,接下来,大家将会到达α太阳系中的科研中心熟悉太空远征舰队中的作战单位,大家将会了解如何面向对象地去操作这些作战单位。此外,恭喜大家晋升少尉!祝大家好运!

PS:听说相控阵雷达可以同时探测和跟踪数百个目标,舰队正在换装中。^_^

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值