1、OC类和对象的创建
一般名词都是类,类是对象的抽象,对象是类的具体实例。
类的设计
1>类名
* 类名的第一个字母必须是大写
* 不能有下划线
* 多个英文单词,用驼峰表识(FlyAir)
2> 属性(成员变量)
3> 行为(功能方法)
当不知道行为该交给哪个类时,选择最了解这个行为的类做这个行为,就像定义电脑这个类,就有行为是开机,初学者感觉这不是人的功能吗?实际上电脑这个类更了解电脑自身开机,人只是调用电脑类的一个对象实现开机,所以开机是电脑类的行为功能。
2、OC类和对象创建实例
/*
类名:Car
属性:轮胎个数、时速(速度)
行为:跑
*/
// 因为使用了NSObject
#import <Foundation/Foundation.h>
// 完整地写一个函数:函数的声明和定义(实现)
// 完整地写一个类:类的声明和实现
// 1.类的声明
// 声明对象的属性、行为
//:NSObject目的是:让Car这个类具备创建对象的能力
@interface Car : NSObject
{
//此处的大括号,用来声明对象属性(实例变量\成员变量,默认会初始化为0)
// @public可以让外部的指针间接访问对象内部的成员变量
@public
int wheels; // 轮胎个数
int speed; // 时速(xxkm/h)
}
// OC的方法和C中的函数写法有很大的区别
// 方法(行为、功能):方法名、参数、返回值(声明、实现)
// 只要是OC对象的方法,必须以减号 - 开头
// OC方法中任何数据类型都必须用小括号()括住
// OC方法中的小括号():括住数据类型
- (void)run;
@end
// 2.类的实现
// 用来实现@interface中声明的方法
@implementation Car
//方法的实现(说清楚方法里面有什么代码)
// 属性操作很像C中的结构体,方法操作很像C中的函数,声明在@interface,实现在@implementation有实现
- (void)run
{
NSLog(@"车子跑起来了");
}
@end
int main()
{
// 在OC中,想执行一些行为,就写上一个中括号[行为执行者 行为名称 ]
// 利用类来创建对象
// 执行了Car这个类的new行为来创建新对象
// 行为执行者为类Car,行为名称是new,创建一个这个类的对象
// 定义了一个指针变量p,p将来指向的是Car类型的对象
// [Car new]每次都会创建出一个新对象,并且会返回新对象本身(新对象的地址)
// OC中对对象的操作是用指针
//成员变量操作类似C语言中的结构体,方法操作类似指向函数的指针
// 就像用结构体类型创建一个变量,只赋给指向这个结构体变量的指针变量,不用其他操作方式
Car *p = [Car new];
Car* p2 = [Car new];
p2->wheels = 5;
p2->speed = 300;
// 实际上,类就像结构体一样,是用户自己创建的新类型,可以说成或想象成C语言中的构造类型。
// OC类和对象的本质就是结构体
// 给p所指向对象的wheels属性赋值,操作和结构体操作一样
p->wheels = 4;
p->speed = 250;
// 给p所指向对象发送一条run消息 (OC消息机制)
[p run];
NSLog(@"车子有%d个轮子,时速为:%dkm/h", p->wheels, p->speed);
return 0;
}
/*
人
类名:Person
属性(成员变量、实例变量):体重weight、年龄
行为(方法):走路
*/
#import <Foundation/Foundation.h>
/*
1.类的声明
*成员变量
*方法的声明
2.类的实现
*/
@interface Person : NSObject
{
//成员变量(必须在大括号里面)
@public
int age;
double weight;
}
// 方法声明;
- (void)walk;
@end
@implementation Person
//实现@interface中声明的方法
- (void)walk
{
NSLog(@"%d岁、%f公斤走路",age ,weight);
}
@end
int main()
{
//对象操作只能用指针操作,因为new创建出来的对象没有名字
//在使用类创建对象之前,会先将类加载进内存,而且只加载一次,所有这个类在内存中是创建最早的
Person* p = [Person new];
p->age = 20;
p->weight = 50.0;
[p walk];//消息机制
// 每个对象内部都有一个默认指针isa,这个指针就指向这个对象所指向的类,类在内存中也要分配内存,只存储方法声明
Person* p2 = [Person new];
p2->age = 30;
p2->weight = 60.0;
[p2 walk];//消息机制,给p2所指向的对象发送walk消息
return 0;
}
3、类和函数使用注意点
#import <Foundation/Foundation.h>
@interface Car : NSObject
{
//成员变量
@public
int wheels;
int speed;
}
- (void)run;
@end
@implementation Car
- (void)run
{
NSLog(@"%d个轮子,速度%dkm/h的车子跑起来", wheels, speed);
}
@end
void test(int w, int s)
{
w = 20;
s = 200;
}
// 只要搞清楚,所有的操作,最终都是对内存的操作,不同的内存互不影响
void test1(Car* newC)
{
newC->wheels = 5;
}
//此函数调用完毕后,指针变量的内存会自动回收,但是所创建的对象内存,不能自动回收,可以手动回收,也可以等到main函数执行完后自动回收
void test2(Car *newC)
{
Car *c2 = [Car new];
c2->wheels = 5;
c2->speed = 300;
newC = c2;
newC->wheels = 6;
}
int main()
{
// 在使用这个类先加载到内存,即先为类分配内存空间,才能用这个类创建对象
Car* c = [Car new];
c->wheels = 4;
c->speed = 250;
test2(c);
test1(c);
test(c->wheels, c->speed);
[c run];
return 0;
}
4、类的合理设计
类的合理设计
/*
学生
成员变量:性别、生日、体重、最喜欢的颜色、狗(体重、毛色)
成员方法:吃、跑步、遛狗(让狗跑,即调用狗跑这个方法)、喂狗(让狗吃,同上)
*/
#import <Foundation/Foundation.h>
typedef struct
{
int year;
int month;
int day;
}Date;
/*
typedef enum sex
{
man,
woman
}mysex;
*/
// 注意:枚举类型的取值,要以枚举类型名开头,易读
typedef enum
{
SexMan,
SexWoman
}Sex ;
typedef enum {
ColorBlack,
ColorRed,
ColorGreen
}Color;
@interface Dog : NSObject
{
@public
double weight;
Color curColor; //毛色
}
- (void) eat;
- (void)run;
@end
@implementation Dog
- (void) eat
{
weight = weight +1;
NSLog(@"狗吃完这次后的体重是%f", weight);
}
- (void)run
{
weight = weight - 1;
NSLog(@"狗跑完这次后的体重是%f", weight);
}
@end
@interface Student : NSObject
{
@public
Sex sex;
Date birthday;
double weight;
Color favColor;
char * name;
// 重点:狗
Dog * dog ;
}
- (void) eat;
- (void)run;
- (void)print;
- (void)liuDog;
- (void)weiDog;
@end
//只有类的声明,编译可以通过,链接通不过,因为没有类的实现,实际和函数的声明和实现有点像
@implementation Student
- (void) eat
{
weight = weight +1;
NSLog(@"学生吃完这次后的体重是%f", weight);
}
- (void)run
{
weight = weight - 1;
NSLog(@"学生跑完这次后的体重是%f", weight);
}
- (void)print
{
NSLog(@"性别=%d, 喜欢的颜色=%d, 姓名=%s,生日=%d-%d-%d",sex,favColor,name,birthday.year,birthday.month,birthday.day);
}
- (void)liuDog
{
//让狗跑起来(调用狗的run方法)
[dog run];
}
- (void)weiDog
{
//让狗吃东西(调用狗的eat方法)
[dog eat];
}
@end
int main()
{
Student* s = [Student new];
//s->dog = [Dog new];
Dog * d = [Dog new];
d->weight = 20;
d->curColor = ColorGreen;
//先创建一个Dog类实例,再赋值给Student类的成员变量Dog类s对象
//只能间接赋值,因为双重对象无法直接赋值
//如果是成员变量是结构体,则可以
s->dog = d;
[s liuDog];
[S weiDog];
return 0;
}
void test()
{
// 让指针访问内部的成员变量,要加关键字@public
s->weight = 50;
// 性别
s->sex = SexMan;
//生日
//s->birthday = {2011, 9, 10};//这种写法错误的
// Date d = {2011, 9, 10};//在定义变量的同时才可以赋值
//结构体的嵌套操作时,符号“->”是指针间接操作时使用,结构体内部的成员操作用符号“.”
/*
//可以,方法一
s->birthday.year = 2011;
s->birthday.month = 9;
s->birthday.day = 10;
*/
//可以,方法二(更简洁)
Date d = {2011, 9, 10};
s->birthday = d;
[s eat];
[s run];
[s print];
}
5、方法的声明和实现
/*
计算器类
*/
#import <Foundation/Foundation.h>
//类名表示规则:第一个字母大写,命名为驼峰方法
@interface JiSuanQi : NSObject
// 方法名:pi
- (double)pi;
// 方法名:pingFang OC中不允许两个方法名一样
//- (int)pingFang;
//OC方法中,一个参数对应一个冒号,冒号放到数据类型前
//方法名:pingFang: 冒号也是方法名的一部分
- (int)pingFang:(int)num;
//- (int)sum : (int)num1 : (int)num2;可读性不好,改进如下
// 方法名:sumWithNum1:andNum2:
- (int)sumWithNum1: (int)num1 andNum2: (int)num2;
@end
@implementation JiSuanQi
- (double)pi
{
return 3.14;
}
//OC方法中,一个参数对应一个冒号,冒号放到数据类型前
//方法命名规则:第一个字母小写,命名为驼峰方法
- (int)pingFang:(int)num
{
return num * num;
}
/*- (int)sum : (int)num1 : (int)num2
{
return num1 + num2;
}*/
- (int)sumWithNum1: (int)num1 andNum2: (int)num2
{
return num1 + num2;
}
@end
int main()
{
JiSuanQi * jsq = [JiSuanQi new];
double a = [jsq pi];
NSLog(@"%f", a);
//冒号的作用是:分隔函数名和参数,否则会分不清0还是10是参数
int b = [jsq pingFang:10];
NSLog(@"%d", b);
int c = [jsq sumWithNum1:10 andNum2:5];
NSLog(@"%i", c);
return 0;
}
6、匿名对象
#import <Foundation/Foundation.h>
@interface Car : NSObject
{
@public
int speed;
}
- (void)run;
@end
@implementation Car
- (void)run
{
NSLog(@"速度是%d的车子跑起来了", speed);
}
@end
int main()
{
//使用指针c1操作这个对象,这个对象是有名对象
Car* c1 = [Car new];
Car* c2;
c2 = [Car new];
c2->speed = 250;
[c2 run];
//下面的类是匿名对象,只要求看懂
//不要写类似匿名对象这样的代码,会造成内存泄露
[Car new]->speed = 300;
[[Car new] run];
return 0;
}
7、编程练习利用前面的成绩类,重新设计一个学生类
1> 属性
* 姓名
* 学号
* 成绩(包括3科成绩)
2> 行为
* 比较C语言成绩:跟另外一个学生比较C语言成绩,返回成绩差(自己的成绩 - 其他人的成绩)
* 比较OC成绩:跟另外一个学生比较OC语言成绩,返回成绩差(自己的成绩 - 其他人的成绩)
* 比较iOS成绩:跟另外一个学生比较iOS语言成绩,返回成绩差(自己的成绩 - 其他人的成绩)
* 比较总分:跟另外一个学生比较总分,返回成绩差(自己的成绩 - 其他人的成绩)
* 比较平均分:跟另外一个学生比较平均分,返回成绩差(自己的成绩 - 其他人的成绩)
*/
#import <Foundation/Foundation.h>
@interface Score : NSObject
{
@public
int CScore;
int OCScore;
int IOSScore;
}
- (int) compareCScoreWithOther:(Score *) score;
- (int) compareSumScoreWithOther:(Score *)score;
@end
@implementation Score
- (int) compareCScoreWithOther:(Score *) score
{
return CScore - score->CScore;
}
- (int) compareSumScoreWithOther:(Score *)score
{
int sum1 = CScore + OCScore + IOSScore;
int sum2 = score->CScore + score->OCScore + score->IOSScore;
return sum1 - sum2;
}
@end
@interface Student : NSObject
{
@public
char* name;
char* number;
Score* score;
}
- (int) compareCScoreWithOther:(Student*) student;
- (int) compareSumScoreWithOther:(Student*) student;
@end
@implementation Student
- (int) compareCScoreWithOther:(Student*) student
{
return [score compareCScoreWithOther:student->score];
}
- (int) compareSumScoreWithOther:(Student*) student
{
return [score compareSumScoreWithOther:student->score];
}
@end
int main()
{
Student * stu1 = [Student new];
Student * stu2 = [Student new];
Score * score1 = [Score new];
Score * score2 = [Score new];
score1->CScore = 100;
score2->CScore = 200;
stu1->score = score1;
stu2->score = score2;
//stu1->score->CScore = 100;//第二层是结构体可以,如果是对象,好像只能间接赋值,如上
//stu2->score->CScore = 200;
int a = [stu1 compareCScoreWithOther:stu2];
int b = [stu1 compareSumScoreWithOther:stu2];
NSLog(@"%d",a);
return 0;
}