- OC是面向对象的编程语言,如何创建和使用对象自然是重中之重,对象是怎么产生的呢?创建对象之前必须要有类,他就像要想盖大楼,必须要有图纸,这个图纸就类似于OC中的类
常用的关键字以及其他英文:
@interface / @end / @implementation / @public / NSLog(@"") / NSObject -framework Foundation / Foundation / Foundation.h
- 和函数一样,类也有声明和实现,类的声明里应该包括对象的属性(成员变量)以及对象的行为(方法),其中方法的声明放在类的声明中,其实类的实现就是方法的实现,以下代码就是创建一个类以及通过类创建对象:
1 //拷贝Foundation框架下的主头文件Foundation.h 2 //import的作用类似include,区别就是:防止重复拷贝 3 #import <Foundation/Foundation.h> 4 5 //类的声明 6 @interface Car : NSObject 7 { 8 @public // 让外部的指针能访问成员变量 9 //实例变量(成员变量) 10 int wheels; // 轮胎 11 int speed; // 速度 12 } 13 14 //对象方法的声明 15 - (void)run; // 不同于函数的声明: ‘-’ 表明是对象方法,以后还会遇到 ‘+’ ,小括号的作用是用来扩住数据类型 16 17 @end // 类的声明结束标志 18 19 // 类的实现 20 @implementation Car 21 22 //方法的实现 23 - (void)run{ 24 25 NSLog(@"轮胎有%d个、速度为%d的车子跑起来了", wheels, speed); 26 } 27 28 @end // 类的实现结束标志 29 30 int main(){ 31 //OC中想要执行一个行为(类的行为以及对象的行为),固定格式为:[行为执行者 行为名称],并且返回的是它在内存中地址 32 //new是类的一个行为,执行完的结果就是在内存中创建了相应的对象 33 Car *p = [Car new]; // 执行完这一句,内存中就有一个Car对象,并且指针p指向该对象 34 35 p->wheels = 4; 36 p->speed = 150; 37 //因为p指向一个Car对象,所以[p run]执行的是一个对象run方法 38 [p run]; 39 return 0; 40 }
想要成功编译链接这个.m文件,必须这么写:cc xxx.m -framework Foundation,其中xxx为文件名运行结果:
上述代码的第33行是利用Car类创建了一个Car对象,如果我想再创建一个Car对象呢?依然还是相同的代码,只不过新的对象和之前的对象在内存中是出于不同的位置将上述代码的main函数改为:
1 int main(){ 2 //OC中想要执行一个行为(类的行为以及对象的行为),固定格式为:[行为执行者 行为名称],并且返回的是它在内存中地址 3 //new是类的一个行为,执行完的结果就是在内存中创建了相应的对象 4 Car *p = [Car new]; // 执行完这一句,内存中就有一个Car对象,并且指针p指向该对象 5 Car *p1 = [Car new]; 6 7 p->wheels = 4; 8 p->speed = 150; 9 10 p1->speed = 200; 11 p1->wheels = 3; 12 //因为p指向一个Car对象,所以[p run]执行的是一个对象run方法 13 [p run]; 14 [p1 run]; 15 16 return 0; 17 }
该程序的运行结果为
- 对于第一个程序中的第38行代码系统式如何运行的呢?当程序第一次使用类的时候,内存中会分配一定的空间给类,并且只会加载一次,并且对象的方法会放在类中,并且每次通过类创建一个对象,这个对象中会有一个isa指针指向该类,一旦调用对象的方法(比如这一题的run方法)它会通过isa指针找到类中的对象方法
- 注意事项:
- 类的声明里不能嵌套其他类的声明或者嵌套自己类的实现。代码如下
1 //错误代码示范:嵌套类的声明 2 @interface Car :NSObject 3 4 ... 5 6 @interface Person :NSObject 7 8 ... 9 10 @end 11 12 @end 13 14 //错误代码示范:嵌套类的实现 15 @interface Car :NSObject 16 17 ... 18 19 @implementation Car 20 21 ... 22 23 @end 24 25 @end
- 类的所有实例变量要用大括号括起来,对象方法的声明不能放在发括号里,要拿出来。并且不能人为初始化,因为系统会自动初始化所有实例变量(比如int、float等类型初始化为0,指针类型的初始化为nil(类似于C语言中的NULL))
1 //错误代码示范 2 @interface Car :NSObject 3 4 { 5 int speed = 100; 6 char *name = "MJ"; 7 } 8 9 @end
- 关于实例变量的使用:实例变量是对象所独有的,也就是说只有创建了对象才能使用实例变量,并且要想指针访问实例变量,必须在实例变量前加上关键字@public
1 //错误代码示范 2 @interface Car :NSObject 3 4 { 5 int speed; 6 7 } 8 9 ... 10 11 @end 12 13 @implementation Car 14 15 ... 16 17 @end 18 19 int main(){ 20 21 speed = 90; // 没有创建对象之前是不能使用实例变量的,即使实例变量前面加了@public 22 ... 23 }
- 类的声明和实现与main函数的位置问题:和C语言中函数的声明和定义一样,类的声明必须放在main函数前面,类的实现可以放在main函数的后面,不同的是,类的声明不不能省略的
- 另外要注意对象方法的声明与之前学过的函数声明的不同之处:对象方法的标志'-'小括号要扩住数据类型例如
1 - (void)run; 2 3 - (int)eat;
- 下面将创建一个Person类以及Dog类并且让Person类里有一个Dog实例变量
1 #import<Foundation/Foundation.h> 2 3 //性别枚举 4 typedef enum{ 5 SexMan, // 男 6 SexWoman // 女 7 } Sex; 8 9 //颜色枚举 10 typedef enum{ 11 ColorBlack, // 黑色 12 ColorRed, // 红色 13 ColorYellow //黄色 14 } Color; 15 16 // 生日结构体 17 typedef struct{ 18 int year; // 年 19 int month; // 月 20 int day; // 日 21 } Date; 22 23 //Dog类的声明 24 @interface Dog : NSObject 25 { 26 @public 27 int weight; 28 Color curColor; 29 } 30 31 - (void)run; // run方法声明 32 - (void)eat; // eat方法声明 33 @end 34 35 //Dog类的实现 36 @implementation Dog 37 38 - (void)run{ 39 weight -= 1; 40 NSLog(@"毛色为%d的狗体重减少为:%d", curColor, weight); 41 } 42 43 - (void)eat{ 44 weight += 1; 45 NSLog(@"毛色为%d的狗体重增加为:%d", curColor, weight); 46 } 47 @end 48 49 // Person类声明 50 @interface Person : NSObject 51 { 52 @public 53 int age; // 年龄 54 int weight; // 体重 55 Sex sex; // 性别 56 Date birthday; // 生日 57 Color favColor; // 最喜欢的颜色 58 Dog *dog; 59 60 } 61 62 - (void)run; // run方法声明 63 - (void)eat; // eat方法声明 64 - (void)printf; // printf方法声明 65 - (void)playWithDog; 66 - (void)feedDog; 67 @end 68 69 //Person类实现 70 @implementation Person 71 72 - (void)run{ 73 weight -= 1; 74 NSLog(@"年龄为%d的人体重减少为:%d", age, weight); 75 } 76 77 - (void)eat{ 78 weight += 1; 79 NSLog(@"年龄为%d的人体重增加为:%d", age, weight); 80 } 81 82 - (void)printf{ 83 NSLog(@"体重为%d-年龄为%d-性别为%d-最爱的颜色为%d-生日为%d/%d/%d", weight, age, sex, favColor, birthday.year, birthday.month, birthday.day); 84 } 85 86 - (void)playWithDog{ 87 [dog run]; 88 } 89 90 - (void)feedDog{ 91 [dog eat]; 92 } 93 @end 94 95 int main(){ 96 //创建Person对象 97 Person *p = [Person new]; 98 99 //初始化Person对象实例变量 100 p->age = 24; 101 p->weight = 60; 102 p->sex = SexMan; 103 p->favColor = ColorYellow; 104 Date d = {2000,4,5}; // 初始化结构体Date变量d 105 p->birthday = d; 106 107 //创建Dog对象 108 Dog *dog = [Dog new]; 109 p->dog = dog; 110 111 //初始化Dog对象实例变量 112 dog->weight = 15; 113 dog->curColor = ColorRed; 114 115 [p playWithDog]; // 调用Person对象的playWithDog方法 116 [p playWithDog]; 117 [p playWithDog]; 118 [p feedDog]; 119 120 [p run]; // 调用Person类的run方法 121 [p run]; 122 [p run]; 123 124 [p eat]; // 调用Person类的eat方法 125 [p printf]; // 调用Person类的printf方法 126 127 return 0; 128 129 }
(也不知道代码能不能想C那样分成多个文件)这样在一个.m文件写下去的话,肯定会让人崩溃的。在写这一段代码中我犯了如下错误:
- @public漏写
- 将Dog类的声明写在了Person类声明的后面,这样会导致编译链接的时候会警告Dog *dog没有定义(估计等学到了多文件开发的时候这个问题就能避免了)
- 漏写了上述代码的第109行,这样会导致Person对象中的dog成员变量指向nil(空),进一步导致第115-118行的所有代码无效