一、面向对象概述
OC为面向对象,C为面向过程,只是两种解决问题的不同的思想方式。
1、面向对象和面向过程的区别
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
面向过程关注的是解决问题需要哪些步骤;面向对象关注的是解决问题需要哪些对象
例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了总多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。
用面向对象思考问题更符合我们人的思考方式。
其实我们人现实生活中都是在面向对象。比如:去饭店吃饭,你只要找服务员,说明吃什么就可以了,有必要还了解这个菜是怎么做的,是哪里来的,怎么去种这个菜吗?
面向对象也可以说是从宏观方面思考问题,而面向过程可以说是从细节处思考问题。在面向对象中,也存在面向过程。
2.常用术语
>面向过程 Procedure Oriented
>面向对象 Object Oriented,简称OO
>面向对象编程 Object Oriented Programming,简称OOP
二、类和对象的关系
面向对象中有2个非常重要的概念:类和对象
1)OC中的类相当于图纸,用来描述一类事物。也就是说,要想创建对象,必须先有类
2)OC利用类来创建对象,对象是类的具体存在
3)因此,面向对象解决问题应该是先考虑需要设计哪些类,再利用类创建多少个对象
1、类的声明和实现
1)类的设计,只关心3样东西:
>事物名称(类名):人(Person)
>属性:身高(height)、年龄(age)
>行为(功能):跑(run)、打架(fight)
2)类的设计:
1> 类名
* 类名的第一个字母必须是大写
* 不能有下划线
* 多个英文单词,用驼峰标识
2> 属性
3> 行为(功能)
3)代码实现
完整地写一个函数:需要函数的声明和定义(实现)
完整地写一个类:类的声明和实现
1.类的声明
声明对象的属性、行为
>成员变量
@interface的大括号{}中声明的变量:wheels、speed
@interface的大括号和函数的大括号是不一样的
默认会初始化为0
@interface Car : NSObject // : NSObject 目的是:让Car这个类具备创建对象的能力
{ // 用来声明对象属性(实例变量\成员变量,默认会初始化为0)
// @public可以让外部的指针间接访问对象内部的成 员变量
@public
int wheels; // 轮胎个数
int speed; // 时速(xxkm/h)
}
// 方法(行为):方法名、参数、返回值(声明、实现)
// 只要是OC对象的方法,必须以减号 - 开头
// OC方法中任何数据类型都必须用小括号()扩住
// OC方法中的小括号():括住数据类型
- (void)run;
@end
2.类的实现
用来实现@inteface中声明的方法
@implementation Car
// 方法的实现(说清楚方法里面有什么代码)
- (void)run
{
NSLog(@"车子跑起来了");
}
@end
3.创建对象
在OC中,想执行一些行为,就写上一个中括号[行为执行者 行为名称],利用类来创建对象
执行Car这个类的new行为来创建新对象, 定义一个指针变量p,p将来指向的是Car类型的对象
[Car new]每次都会创建出一个新对象,并且会返回新对象本身(新对象的地址),那么就应该用一个指针变量保存对象的地址
用一个指针变量c指向内存中的Car对象
Car *p = [Car new];
// 给p所指向对象的wheels属性赋值
p->wheels = 4;
p->speed = 250;
// 给p所指向对象发送一条run消息
[p run];
NSLog(@"车子有%d个轮子,时速位:%dkm/h", p->wheels, p->speed);
注意:
>成员变量不能在{}中进行初始化、不能被直接拿出去访问
>方法不能当做函数一样调用
>成员变量\方法不能用static等关键字修饰,别跟C语言混在一起
>类的实现可用写在main函数的后面,主要声明在前面就行了
错误:
>只有类的声明,没有类的实现
>漏了@end
>@interface和@implementation嵌套
>两个类的声明嵌套
>成员变量没有写在括号里面
>方法的声明写在了大括号里面
细节:
>方法只有声明,没有实现(经典错误)
>方法没有声明,只有实现(编译器警告,但是能调用,OC的弱语法)
>编译的时候:访问没有的成员变量直接报错,访问没有的方法,只是警告
4.完整代码显示
/*
人
类名:Person
属性(成员变量\实例变量):体重、年龄
行为(方法):走路、吃
*/
#import <Foundation/Foundation.h>
/*
1.类的声明
* 成员变量
* 方法的声明
*/
@interface Person : NSObject
{
@public
int age;
double weight;
}
- (void)walk;
- (void)eat;
@end
// 2.类的实现
@implementation Person
// 实现@interface中声明的方法
- (void)walk
{
NSLog(@"%d岁、%f公斤的人走了一段路", age, weight);
}
- (void)eat
{
NSLog(@"%d岁、%f公斤的人在吃东西", age, weight);
}
@end
int main()
{
// 在使用类创建对象之前,会将类加载进内存
Person *p = [Person new];
p->age = 20;
p->weight = 40;
[p eat];
[p walk];
Person *p2 = [Person new];
p2->age = 30;
p2->weight = 60;
[p2 eat];
[p2 walk];
return 0;
}
内存分析
1)方法
1.对象方法都是以减号 -
2.对象方法的声明必须写在@interface和@end之间
对象方法的实现必须写在@implementation和@end之间
3.对象方法只能由对象来调用
4.对象方法归类\对象所有
2)函数
1.函数能写在文件中的任意位置(@interface和@end之间除外),函数归文件所有
2.函数调用不依赖于对象
3.函数内部不能直接通过成员变量名访问某个对象的成员变量
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
@implementation Person
@end
@interface Car : NSObject
{// 成员变量\实例变量
//int wheels = 4; 不允许在这里初始化
//static int wheels; 不能随便将成员变量当做C语言中的变量来使用
@public
int wheels;
}
- (void)run;
- (void)fly;
@end
int main()
{
// wheels = 10;
/*
Car *c = [Car new];
c->wheels = 4;
//run();
[c run];
*/
void test2();
test2();
return 0;
}
@implementation Car
- (void) fly
{
}
/*
void test2()
{
NSLog(@"调用了test2函数-%d", wheels);
}*/
void test()
{
NSLog(@"调用了test函数");
}
- (void)run
{
test();
NSLog(@"%d个轮子的车跑起来了", wheels);
}
@end
四、
类的合理设计
/*
学生
成员变量:性别、生日、体重、狗(体重、毛色,吃、跑)
方法:吃、跑步、遛狗(让狗跑)、喂狗(让狗吃)
*/
#import <Foundation/Foundation.h>
typedef enum { //枚举
SexMan,
SexWoman
} Sex;
typedef struct { //结构体
int year;
int month;
int day;
} Date;
//声明---------狗----------------
@interface Dog : NSObject
{
@public
double weight; // 体重
}
- (void)eat;
- (void)run;
@end
//实现-----------狗--------------
@implementation Dog
- (void)eat
{
// 每吃一次,体重就加1
weight += 1;
//weight = weight + 1;
NSLog(@"狗吃完这次后的体重是%f", weight);
}
- (void)run
{
weight -= 1;
NSLog(@"狗跑完这次后的体重是%f", weight);
}
@end
//声明--------学生-----------
@interface Student : NSObject
{
@public
Sex sex; // 性别
Date birthday; // 生日
double weight; // 体重(kg)
// 重点:狗
Dog *dog;
}
- (void)eat;
- (void)run;
- (void)print;
- (void)liuDog;
- (void)weiDog;
@end
//实现----------学生--------------
@implementation Student
- (void)liuDog
{
// 让狗跑起来(调用狗的run方法)
[dog run];
}
- (void)weiDog
{
// 让狗吃东西(调用狗的eat方法)
[dog eat];
}
- (void)print
{
NSLog(@"性别=%d, 姓名=%s, 生日=%d-%d-%d", sex,birthday.year, birthday.month, birthday.day);
}
- (void)eat
{
// 每吃一次,体重就加1
weight += 1;
//weight = weight + 1;
NSLog(@"学生吃完这次后的体重是%f", weight);
}
- (void)run
{
weight -= 1;
NSLog(@"学生跑完这次后的体重是%f", weight);
}
@end
int main()
{
Student *s = [Student new];
Dog *d = [Dog new];
d->curColor = ColorGreen;
d->weight = 20;
s->dog = d;
[s liuDog];
[s weiDog];
return 0;
}
void test()
{
Student *s = [Student new];
s->weight = 50;
// 性别
s->sex = SexMan;
// 生日
Date d = {2011, 9, 10};
s->birthday = d;
s->name = "Jack";
/*
s->birthday.year = 2011;
s->birthday.month = 9;
s->birthday.day = 10;
*/
[s print];
}
五、带参数的方法
1.方法声明
单个参数:
- (方法返回值类型) 方法名:(参数类型) 参数名;
OC方法中,一个参数对应一个冒号,冒号也是方法名的一部分
- (int)pingFang:(int)num;
多个参数:
- (方法返回值类型) 方法名:(参数类型)参数名1 方法名:(参数类型)参数名2;
方法名严格按照参数名的定义形式,看上去简单明了。
//方法名:sumWithNum1:andNum2:
- (int)sumWithNum1:(int)num1 andNum2:(int)num2;
- (int)sumWithNum1:(int)num1 andNum2:(int)num2 andNum3:(int)num3;
2.方法实现
- (int)pingFang:(int)num
{
return num * num;
}
- (int)sumWithNum1:(int)num1 andNum2:(int)num2
{
return num1 + num2;
}
3.方法调用
JiSuanQi *jsq = [JiSuanQi new];
int a = [jsq sumWithNum1:20 andNum2:5];
六、匿名对象
用指针创建对象:
Car *c = [Car new];
c->speed = 250;
[c run];
匿名对象:
[Car new]->speed = 300;
[[Car new] run];
直接用对象指向成员变量,调用方法,每次执行[Car new]都会创建一个新对象,每个操作都在新创建的对象中,浪费系统系能。不要写这样的代码!