Objective-C学习笔记(第二天)

前言:学习视频是iOS开发基础班+就业班(100天完整版)之基础班2:Objective-C学习(10天)_哔哩哔哩_bilibili
学习笔记是转载自我的Objective-C语言学习笔记(四) – BeautyLife Studio
因为本人学习到第三天时才看上面的学习笔记,发现笔记内容和课程顺序是一模一样,本人写的笔记也大差不差,但是我的示例代码会缩减,所以还是转载他人的,以供他人学习

可能会说现在怎么还有人学ios,我也觉得(狗头),但是公司业务有涉及,来看一些快速上手文章也是好的,技多不压身嘛。时间足够的可以结合视频食用效果更佳,时间短也能通过文章有所了解。

2.0 类与对象

2.1 对象在内存中的存储

我在《简明C语言学习指南》中提到过内存有五大区域,分别是:

 存储局部变量,例如[Person *p1],创建了Person类的指针变量p1,此时p1存储在栈中,指向类对象在堆中的地址
程序员手动申请的字节空间,例如[Person new],申请创建对象,isa指针指向类在代码段的地址
BSS段存储未被初始化的全局变量 静态变量
数据段/常量区存储已被初始化的全局 静态变量 常量数据
代码段存储代码。存储程序的代码,例如@interface Person

接下来,我们要谈谈类加载。

什么叫做类加载呢?

我们可以想象,在创建对象的时候,肯定是需要访问类的,声明一个类的指针变量也会访问类的。

当程序运行期间,当某个类第一次被访问到的时候,会将这个类存储到内存中的代码段区域,这个过程就叫做类加载。

只有类在第一次被访问的时候,才会做类加载。

一旦类被加载到代码段以后,直到程序结束的时候才会被释放。

那么对象在内存中又是如何存储的呢?

假设下面这句代码写在函数之中:

Person *p1 = [Person new];

Person *p1 会在栈内存中申请一个空间,在栈内存中声明一个Person类型的指针变量p1。p1是一个指针变量,那么只能存储地址。

[Person new],真正在内存中创建对象的其实是这句代码。

new做的事情,

第一是在堆内存中申请1块合适大小的空间;

第二是在这个空间中根据类的模板创建对象,类模板中定义了什么属性,就把这些属性依次的声明在对象之中,对象中还有另外一个属性,叫做isa,它是一个指针,这个指针指向这个对象所属的类在代码段中的地址;

第三是初始化对象的属性。如果属性的类型是基本数据类型,那么赋值为0,如果属性的类型是C语言的指针类型,那么就赋值为NULL,如果属性的类型是OC的类指针类型,那么就赋值为nil;

第四是返回对象的地址,将这个地址赋值给p1。

注意:

对象中只有属性,而没有方法。只有自己所属类的属性外加一个isa指针指向代码段中的类;

如何访问对象的属性

指针名(对象名)->属性名;

根据指针找到指针指向的对象,再找到对象中的属性来访问

如何调用方法

[指针名(对象名) 方法名];

先根据指针名找到对象,对象发现要调用方法,再根据对象的isa指针找到类,然后调用类里的方法

那为什么不把方法存储在对象中?

因为每一个对象的方法的代码实现都是一模一样的,没有必要为每一个对象都保存一个方法,这样的话就太浪费空间了,既然都一样,那么就保存一份就可以了。

2.2 nil和NULL

NULL

可以作为指针变量的值,如果一个指针变量的值是NULL值代表这个指针不指向内存中的任何空间。NULL其实等价于0,它的本质是一个定义为0的宏。

nil

只能作为指针变量的值。代表这个指针变量不指向内存中的任何空间。nil其实也等价于0,它的本质也是一个宏。

所以,我们可以认为NULL和nil其实是一样的。

使用建议:

虽然NULL和nil是等价的,但并不建议随便互换使用。

一般来说C指针用NULL,而OC的类指针用nil。

例如:

int *p1 = NULL;

p1指针不指向内存中的任何一块空间

Person *p1 = nil;

p1类指针不指向任何对象。

如果一个类指针的值是nil,而这个时候又给这个类指针的属性赋值,那就会报错。

例如:

Person *p1 = nil;
p1->_name = @“Jack”;
p1->_age = 19;

后两句一编译就会报错

另外,如果这个时候通过p1类指针去调用对象的方法,运行虽然不会报错,但是方法不会执行,没有任何反应。

2.3 多个指针指向同一个对象

同类型的指针变量之间是可以相互赋值的。

Person *p1 = [Person new];
Person *p2 = p1;

这样做是完全没有问题的,p1和p2的类型都是Person指针类型的,代表将p1的值赋值给p2,而p1的值是对象的地址,所以就把对象的地址赋值给了p2,p1和p2指向了同一个对象。

无论通过p1还是p2去修改对象,都是修改的同一个对象。

2.4 分组导航标记

当我们在定义类的时候,如果一个两个还好,一旦如果需要定义很多个类的时候,在寻找类的代码就比较繁琐了。

这个时候我们可以使用分组导航标记。

方法:

#pragma mark 分组名

这样就会在导航条对应的位置显示一个标题

#pragma mark -

这样就会在导航条对应的位置显示一条水平分隔线

#pragma mark - 分组名

这样就会在导航条对应的位置先产生一条水平分隔线,再显示标题。

2.5 函数与方法的对比

我们之前学习了函数,例如:

void test()
{

}

如今在OC的类中,我们又学习了方法:

-(void)sayHi;

我们会觉得这两种代码有类似的功能,那么它们到底有何异同点呢?

首先,它们都是用来封装一段代码,将一段代码封装在其中,表达一个相对独立的功能。函数或者方法只要被调用,那么封装在其中的代码就会被自动执行。

那么,它们的不同点是:

语法不同

定义的位置不一样

OC方法的声明只能写在@interface的大括号外面,实现只能写在@implementation之中。

函数除了在函数的内部和@interface的大括号之中,,其他的地方都可以写。(虽然函数可以在很多地方写,但是一般来说是不会在类中出现的,虽然不报错,但这样做很不规范)

调用的方法也不一样

函数是可以直接调用的

方法必须要先创建对象,通过对象才能调用

方法是属于类的,但是函数是独立的,不属于其他任何结构。

2.6 容易犯的错

这一节我们来谈谈在使用类和对象的时候容易犯的错。

@interface是类的声明,@implementation是类的实现,它们之间是不能互相嵌套的;

类必须先声明,后实现;

类的声明和实现都必须同时存在(可以没有方法);

这里多说一句,类的实现可以放在main函数之后,也就是说可以放在使用这个类的代码之后。但是类的声明必须在main函数之前,也就是在使用这个类的代码之前。

属性名一定要以下划线开头,这是规范。否则在学习后面的知识点会出现问题;

类的名字首字母必须要大写,这也是规范;

属性不允许在声明的时候进行初始化;

OC的方法必须要先创建对象后才能被使用;

OC的方法有声明就必须要有实现。

如果方法只有声明没有实现,编译器会给一个警告;

如果指针指向的对象只有方法的声明,而没有方法的实现的话,那么这个时候通过指针来调用这个方法在运行的时候就会报错。

报错内容为:

unrecognized selector sent to instance 内存地址。

2.7 多文件开发

学习OC到现在,我们已经练习了写学多类的声明和实现,我们发现当类的定义很多个的时候,查找代码就比较困难。这个时候我们想起了C语言中的多文件开发,而OC肯定也是有这个功能的。

我们可以把类写在模块中,在主文件中调用即可。即方便了代码的管理,也可以实现多人团队合作开发。

一般来说,把一个类写在一个模块中,我们需要创建两个文件:

.h 头文件

用来写类的声明,因为要用到Foundation框架中的类NSObject,所以在这个头文件中要引入Foundation框架的头文件,然后将类的声明部分写在头文件中。

.m 代码文件

用来写类的实现,先引入模块的头文件,这样才会有类的声明,再写上类的实现。

然后在主文件中,要用到这个类的话,只需要引入这个类模块的头文件就可以直接使用了。

还有一个添加类模块的更简洁的方式:

在选择创建新文件时,选择Cocoa Class,用这种方式创建的头文件和代码文件会自动将类的声明和实现写进相应的文件中。且类的名字和创建文件的时候建立的文件名一致。(当然,事后也是可以更改名字的)

那么从现在开始,所有的类都要这样创建。

2.8 对象与方法

对象可以作为方法的参数也可以作为方法的返回值。

其实,类的本质是我们自定义的一个数据类型。

因为对象在内存中的大小是由我们决定的,多写几个属性,对象占用的空间就大一些,少写几个属性对象就小一些。

那么,既然类是一个数据类型,那么作为类的对象就可以作为方法的参数。

例如可以这么写:

有这样一个类:

@interface Person : NSObject
{
	NSString *_name;
	int _age;
}
-(void)run;
@end

@implementation Person
-(void)run
{
	NSLog(@“跑啊跑啊!”);
}
@end
还有另外一个类:
@interface Girl : NSObject
{
	//此处省略属性声明
}
-(void)test:(Person *)person;
@end

@implementation Girl
-(void)test:(Person *)person
{
	[person run];
}
@end

这就是利用对象来作为方法的参数。注意:对象作为参数,参数类型应该是类名 *。

然后在main函数中可以如此操作:

Girl *g1 = [Girl new];
Person *p1 = [Person new];
[g1 test:p1];

这样就完成了调用。

当对象作为方法的参数的时候,需要注意:

参数类型是类指针即类名 *

调用方法的时候,如果方法的参数是一个对象,那么给实参的时候,实参要求也必须是一个符合要求的对象。即在刚才的例子中,作为参数的对象是属于Person类的,那么传入的实参也必须是Person类的对象,而不能是Girl类的对象。

当对象作为方法的参数传递值的时候,是地址传递。所以在方法内部通过形参去修改形参指向的对象的时候,会影响实参变量指向的对象的值。

下面举例说明什么时候可以用到将对象作为方法的参数:

假设有两个类,分别是神和人类。

神类:

属性:名字,年龄,性别

行为:增加或减少人类的寿命

人类:

属性:姓名,年龄,性别,剩余寿命

行为:展示自己的信息

根据这个提示,我们开始创建类。新建两组头文件和实现文件,分别是God和Person。另外,为了方便定义性别的属性,我们还建立一个头文件gender.h来定义枚举。

gender.h的代码如下:

#ifndef Gender_h
#define Gender_h

typedef enum
{
    GenderMale,
    GenderFemale
}Gender;

#endif /* Gender_h */

然后是Person.h代码如下:

#import <Foundation/Foundation.h>
#import "Gender.h"

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
{
    @public
    NSString *_name;
    int _age;
    Gender _gender;
    int _leftLife;
}
-(void)show;
@end

NS_ASSUME_NONNULL_END

Person.m的代码如下:
#import "Person.h"

@implementation Person
-(void)show
{
    NSLog(@"我叫%@,我还有%d年可以活。",_name,_leftLife);
}
@end

然后是God.h代码如下:

#import <Foundation/Foundation.h>
#import "Gender.h"
#import "Person.h"

NS_ASSUME_NONNULL_BEGIN

@interface God : NSObject
{
    @public
    NSString *_name;
    int _age;
    Gender _gender;
}
-(void)addLifeWith:(Person *)person;
-(void)reduceLifeWith:(Person *)person;
@end

NS_ASSUME_NONNULL_END

God.m的代码如下:

#import "God.h"

@implementation God
-(void)addLifeWith:(Person *)person
{
    person->_leftLife += 5;
    NSLog(@"喝下这杯圣水,凡人你将得到救赎……");
    NSLog(@"名字叫做%@的人,寿命增加至%d年",person->_name,person->_leftLife);
}
-(void)reduceLifeWith:(Person *)person
{
    person->_leftLife -= 5;
    NSLog(@"喝下这杯圣水,凡人你将得到惩罚……");
    NSLog(@"名字叫做%@的人,寿命减少至%d年",person->_name,person->_leftLife);
}

@end

最后是main函数的代码:

#import <Foundation/Foundation.h>
#import "God.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        God *g1 = [God new];
        g1->_name = @"上帝";
        g1->_age = 99999;
        g1->_gender = GenderMale;

        Person *p1 = [Person new];
        p1->_name = @"Bill";
        p1->_age = 55;
        p1->_leftLife = 6;
        p1->_gender = GenderMale;

        Person *p2 = [Person new];
        p2->_name = @"Jack";
        p2->_age = 22;
        p2->_gender = GenderMale;
        p2->_leftLife = 40;

        [g1 addLifeWith:p2];
        [g1 reduceLifeWith:p1];
        [p1 show];
        [p2 show];

    }
    return 0;
}

以上便是一个利用对象作为方法的参数的示例。大家可以尝试在Xcode中输入查看运行效果。

另外,我们再举一个例子来说明将本类对象作为本类方法的参数,这种情况。

例如,我们想要计算平面上两个点之间的距离,我们可以建立这样的一个类——点,具有的功能(方法)是可以计算两个点之间的距离。

先建立一个类:

@interface Point : NSObject
{
	@public
	int _x;
	int _y;
}
-(double)distanceWithOtherPoint:(Point *)otherPoint;
@end

这个类的实现,可以写成:

#import <math.h>

@implementation Point
-(double)distanceWithOtherPoint:(Point *)otherPoint
{
	double res1 = (_x-otherPoint->_x)*(_x-otherPoint->_x);
	double res2 = (_y-otherPoint->_y)*(_y-otherPoint->_y);
	double res3 = res1 + res2;
	return sqrt(res3);
}
@end

下面来谈谈对象作为方法的返回值。

什么时候方法的返回值是一个对象呢?

当方法执行完毕之后,如果有一个对象方法的内部不知道如何处理,并且这个对象是调用者需要使用的,那么这个时候我们就应该将这个对象返回。

那么如何返回对象呢?

再次利用Person和Girl的类的例子,如果Girl类的方法需要返回一个对象,格式应该是:

-(Person *)test1;

实现的格式可以是:

-(Person *)test1
{
	Person *Mary = [Person new];
	Mary->_name = @“Mary”;
	Mary->_age = 19;
	return Mary;
}

注意:返回值的类型应该是一个类指针,而return后应该跟对象名。

在main函数中接受这个返回值时,格式一般如下:

Person *p1 = [g1 test1]

用于接受这个返回值的类型应该也是一个类指针。

示例还是举刚才那个神与人的案例:

我们可以在God.h中增加一个方法为造人:

-(Person *)makePerson;

在God.m中增加造人方法的实现:

-(Person *)makePerson
{
	Person *p1 = [Person new];
	p1->_name = @“Eva”;
	p1->_age = 0;
	p1->_gender = GenderFemale;
	p1->_leftLife = 100;
	return p1;
}

在main函数中可以增加以下两句代码来接收这个方法的返回值:

	Person *p3 = [g1 makePerson];
	[p3 show];

大家可以再尝试一下在原来代码中添加这些代码后运行一下查看结果。

但是我们会发现,刚才代码虽然执行没有问题,但是却有一些局限性,即每次上帝造人都是造的同一个人Eva,不能自定义。那要根据我们的需求造人呢?

对!我们可以把这个代码改造一下,增加一些参数:

-(Person *)makePersonWithName:(NSString *)name andAge:(int)age andGender:(Gender)gender andLeftLife:(int)leftLife;

这个方法的实现可以写成:

-(Person *)makePersonWithName:(NSString *)name andAge:(int)age andGender:(Gender)gender andLeftLife:(int)leftLife
{
	Person *p1 = [Person new];
	p1->_name = name;
	p1->_age = age;
	p1->_gender = gender;
	p1->_leftLife = leftLife;
	return p1;
}

然后在main函数中修改两句代码为:

Person *p3 = [g1 makePersonWithName:@"Eva" andAge:0 andGender:GenderFemale andLeftLife:100];
[p3 show];

请大家再尝试一下把刚才的代码替换成这些后,在Xcode中运行看一下结果,是不是更合适了呢?

这就是对象作为方法的返回值的介绍。

2.9 类的属性和方法

类的属性代表什么?

类的属性代表这类事物具有的共同的特征。

类的属性代表这个类所拥有的东西。

如何分析类有哪些属性?

就分析这个类拥有哪些东西,拥有的东西就可以作为类的属性。

例如:

电灯,它拥有品牌、形状、功率、价格、使用寿命等,而这些它拥有的东西也正是它的属性。

类的方法代表什么?

代表这个类所具备的行为,这个类所具备的功能。

一个类有什么方法,就去分析这个类有什么功能。

例如:

电灯所拥有的功能:发光

现在我们来谈谈对象作为类的属性。

有这样一个案例:

首先人是一个类,而宠物猫咪也是一个类。那么人是可以拥有一只猫咪的,这个时候这个猫咪就是人的一个属性。

首先,我们先创建一个猫咪类。

@interface Cat:NSObject
{
		NSString *_name;
		NSString *_color;
		int _age;
}
@end
@implementation Cat
@end

然后我们再创建一个人类:

@interface Person:NSObject
{
		NSString *_name;
		int _age;
		float _height;
		float _weight;
		Cat *_cat;
}
@end
@implementation Person
@end

我们在人类的属性里加上了一句代码为Cat *_cat;这表示我们将猫咪类中的一个对象作为了人类中的一个属性。声明的是一个指针,指向的是一个属于猫咪类的对象。

我们可以看出,属性的本质其实是变量。

如果对象的属性是另外一个类的对象,这个属性仅仅是一个指针变量而已,并没有产生具体的对象。所以我们虽然建立了一个猫咪对象作为人类的属性,但是在我们创建一个具体的猫咪对象前,这个属性是不指向任何对象的,它的值是nil。

我们来总结一下:

A类可以作为B类的属性,代表B拥有A。

但这个属性仅仅是一个变量而已,是一个指针变量,默认值是nil,并没有创建对象。

这个时候如果要正常的使用的话,必须要创建A类的一个具体的对象。

2.10 案例——猜拳游戏

现在我们通过一个案例来演示面向对象的程序设计。

有些代码可能因为我们只学习到这个程度的原因而阅读起来有些困难,大家可以不必在意,在学习完本书所有内容后再回过来看就会一看就明白了。

好,这个案例就是“猜拳游戏”。

在猜拳游戏中,会设置两个人物,一个是玩家,一个是电脑本身。玩家通过输入来表达自己出石头、剪刀和布,而电脑也会随机产生相应的石头、剪刀或布应对,会有一个裁判来进行每局比赛的判定,然后记录并输出累积比赛结果。

我相信用C语言来写的话,大家应该都没有问题。但是用OC要怎么写呢?

那我们来分析一下要如何用OC语言来完成这个小游戏的程序设计。

猜拳游戏的流程

  1. 玩家出拳
  2. 机器人出拳
  3. 裁判宣布比赛结果
  4. 记录结果

就是以上这几步重复进行。

面向对象的重点在于找出所有的类

玩家类

属性:姓名、选择的拳头、得分

方法:出拳->让用户自己选择

机器人类

属性:姓名、出的拳头、得分

方法:出拳->随机出拳

裁判类

属性:姓名

方法:判断输赢,并显示记录分数

先建立玩家类:

我们发现在建立属性中出的拳头的时候,只有剪刀、石头和布三种,且玩家和机器人都有这个属性,所以适合建立一个头文件,把这三种拳头设置为枚举。

于是我们先建立一个叫Quantou.h的头文件,在里面写下如下代码:

#ifndef Quantou_h
#define Quantou_h

typedef enum
{
    QuantouJianDao = 2,
    QuantouShiTou = 0,
    QuantouBu = 1
}Quantou;


#endif /* Quantou_h */

然后把这个头文件引入到玩家类的头文件Player.h中。

Player.h的内容如下:

#import <Foundation/Foundation.h>
#import "Quantou.h"

NS_ASSUME_NONNULL_BEGIN

@interface Player : NSObject
{
    @public
    NSString *_name;
    Quantou _selectedQuantou;
    int _score;
}
-(void)showQuantou;
-(NSString *)QuantouWithNumber:(int)number;

@end

NS_ASSUME_NONNULL_END

Player.m内容:

出拳的方法应该有如下几步:

先提示玩家选择拳头

接收玩家输入的拳头

显示玩家选择的拳头

将玩家选择的拳头存储到当前对象的属性中

另外因为直接输出显示玩家选择的拳头会只显示0、1或2这样的数字,所以我们还需要建一个方法来进行转换。因为在方法中如果想要调用当前类的另外一个方法,需要用到如下语法:

[self 方法名];

另外还要注意,玩家输入的时候可能会输入不符合语法的内容,即输入了除0、1、2以外的数字,所以要加入一句if结构来防止这样的输入。

最终的代码如下:

#import "Player.h"

@implementation Player
-(void)showQuantou
{
loop:
    NSLog(@"亲爱的玩家[%@]请选择你要出的拳头:0代表石头,1代表布,2代表剪刀",_name);
    int playerSelect = 3;
    scanf("%d",&playerSelect);
    NSString *result = [self QuantouWithNumber:playerSelect];
    if (playerSelect != 0 && playerSelect != 1 && playerSelect != 2)
    {
        NSLog(@"出拳错误!请重试!");
        goto loop;
    }
    NSLog(@"玩家[%@]出的拳头是:%@",_name,result);
    _selectedQuantou = playerSelect;
}
-(NSString *)QuantouWithNumber:(int)number
{
    switch (number)
    {
        case 0:
            return @"石头";
        case 1:
            return @"布";
        case 2:
            return @"剪刀";
        default:
            return @"出拳错误!";
    }
}
@end

这样就完成了玩家类的设计。

接下来是机器人类的设计:

同样是两个文件,一个头文件Robot.h和实现文件Robot.m。

在Robot.h中写下如下代码:

Robot的属性和玩家的属性是一样的,所以可以复制一份,但是方法不一样,随机出拳的代码需要重新写。

另外,机器人随机出拳后得到的结果也是一个0、1、2中的数字,想要显示具体的出拳种类,还需要一个转换的方法,这个转换的方法和玩家是一样的,所以可以借鉴过来。

于是,

Robot.h的内容为:

#import <Foundation/Foundation.h>
#import "Quantou.h"

NS_ASSUME_NONNULL_BEGIN

@interface Robot : NSObject
{
    @public
    NSString *_name;
    Quantou _selectedQuantou;
    int _score;
}
-(void)showQuantou;
-(NSString *)QuantouWithNumber:(int)number;
@end

NS_ASSUME_NONNULL_END

Robot.m的内容为:

#import "Robot.h"
#import <stdlib.h>

@implementation Robot

-(void)showQuantou
{
    int robotSelect = arc4random_uniform(3);
    NSString *result = [self QuantouWithNumber:robotSelect];
    NSLog(@"机器人[%@]出的拳头是:%@",_name,result);
    _selectedQuantou = robotSelect;
}

-(NSString *)QuantouWithNumber:(int)number
{
    switch (number)
    {
        case 0:
            return @"石头";
        case 1:
            return @"布";
        case 2:
            return @"剪刀";
        default:
            return @"出拳错误!";
    }
}
@end

以上就完成了机器人类的设计。

最后是裁判类的设计:

当然也是两个文件,一个是Judge.h,另一个是Judge.m。

裁判的属性比较简单,只有一个名字,但是方法比较复杂,需要引入两个参数,分别是玩家的出拳和机器人的出拳。然后比较这两个参数,得到结果,并记录进总分。

于是,

Judge.h的内容如下:

#import <Foundation/Foundation.h>
#import "Player.h"
#import "Robot.h"

NS_ASSUME_NONNULL_BEGIN

@interface Judge : NSObject
{
    @public
    NSString *_name;
}
-(void)judgeWithPlay:(Player *)player andRobot:(Robot *)robot;

@end

NS_ASSUME_NONNULL_END

Judge.m的内容:

这里需要注意的是判断输赢的条件,我们可以通过判断语句设定“当玩家出布、机器人出石头或者玩家出剪刀、机器人出布或者玩家出石头、机器人出剪刀的情况下玩家赢”,但是这样的语句比较繁琐,而且写起来很长不便于查看,所以为了遵循简化原则,我们分析一下会发现:

在设定枚举的时候,我们定义了剪刀是2,布是1,石头是0。

那么当玩家出剪刀,机器人出布时,计算机拿到的数据其实是2和1;

同理玩家出布,机器人出石头时,计算机拿到的数据是1和0;

玩家出石头,机器人出剪刀时,计算机拿到的数据是0和2。

这三种情况下,两者的差值分别是1、1和-2,这个时候玩家是赢的。

同样道理,我们可以得到,在两者差值为-1、2的情况下,机器人是赢的。

当两者差值为0时,是平局。

所以最终代码可以这么写:

#import "Judge.h"

@implementation Judge
-(void)judgeWithPlay:(Player *)player andRobot:(Robot *)robot
{
    Quantou playerSelect = player->_selectedQuantou;
    Quantou robotSelect = robot->_selectedQuantou;
    int result = playerSelect - robotSelect;
    NSLog(@"我是裁判[%@],我来宣布结果……",_name);
    if (result == 1 || result == -2)
    {
        NSLog(@"恭喜玩家[%@]取得了胜利!",player->_name);
        player->_score++;
    }
    else if (result == -1 || result == 2)
    {
        NSLog(@"恭喜机器人[%@]取得了胜利!",robot->_name);
        robot->_score++;
    }
    else if (result == 0)
    {
        NSLog(@"很遗憾,这局是平局!");
    }
    NSLog(@"玩家[%@]胜%d局 VS 机器人[%@]胜%d局",
          player->_name,
          player->_score,
          robot->_name,
          robot->_score);
}

@end

在写完了所有的类设计之后,我们来完成main函数的内容。

main函数比较简单,只需要分别创建玩家、机器人和裁判各一个对象即可。

在完成了对象的创建以后,我们需要使用一个循环结构来实现反复玩这个游戏的目的,最后用一个if结构来完成退出的目的。当然,在一开始别忘记把头文件引入。

代码如下:

#import <Foundation/Foundation.h>
#import "Player.h"
#import "Robot.h"
#import "Judge.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        Player *xiaoMing = [Player new];
        xiaoMing->_name = @"小明";

        Robot *alphaGo = [Robot new];
        alphaGo->_name = @"阿尔法?";

        Judge *judge = [Judge new];
        judge->_name = @"黑哨";

        while (1)
        {
            [xiaoMing showQuantou];
            NSLog(@"轮到机器人[%@]出拳……",alphaGo->_name);
            [alphaGo showQuantou];
            [judge judgeWithPlay:xiaoMing andRobot:alphaGo];

            NSLog(@"你还想再玩一把吗?Y/N");
            char ans = 'a';
            rewind(stdin);
            scanf("%c",&ans);
            if (ans != 'y' && ans != 'Y' )
            {
                NSLog(@"游戏已结束,欢迎下次再来!");
                break;
            }
        }

    }
    return 0;
}

好了,这样就完成了这个简单的猜拳游戏,大家运行起来看看效果吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值