首页 > 代码库 > objective-c学习笔记(2)
objective-c学习笔记(2)
面向对象和面向过程思想
OC是面向对象的,C是面向过程的。面向对象和面向过程只是解决问题的两种不同思想
面向对象和面向过程的区别
以用电脑听歌为例子
面向过程关注的是解决问题的步骤,数据和操作分离
- 打开电脑
- 播放电脑中的歌曲
- 关闭电脑
面向对象,Everything is an object!一切都是对象,数据和操作不分离,思考解决问题需要多少个对象,不用分析需要多少步骤解决问题
- 电脑
- 开机
- 播放歌曲
- 关机
区别分析
- 面向过程关注的是解决问题需要哪些步骤
- 面向对象关注的是解决问题需要哪些对象
- 没有开发经验很难感受到它们的区别,两种思想都能达到解决问题的目的,但是解决思路不一样
现实生活中面向对象的例子
- 想打电话、发短信 》 找手机,不用分析电话要怎样才能拨通
- 去饭店吃饭 》 找服务员,不用分析怎么到厨房炒菜
- 汽车坏了 》 找汽车维修工,不用分析修车的步骤
- 境界:万物皆对象
常用术语
- 面向过程 Procedure Oriented
- 面向对象 Object Oriented,简称OO
- 面向对象编程 Object Oriented Programming,简称OOP
类和对象的关系
面向对象中有2个非常重要的概念:类和对象
如何创建对象
1) 面向对象解决问题的时候必须有对象
2) 现实生活的例子:如何创造汽车对象?
a) 需要先有汽车的建造图纸,图纸上描述清楚汽车应该具备的属性和功能(行为)
- 属性:轮子数、时速
- 功能(行为):跑
b) 然后再根据图纸上的描述生成汽车
c) 每一辆汽车都是对象,都有自己具体的属性值,都是图纸的实例
d) 图纸是抽象的,汽车是具体的。图纸是对汽车对象的高度概括
OC中的面相对象
1) 类相当于图纸,用来描述一类事物。也就是说,要想创建对象,必须先有类
2) 利用类来创建对象,对象是类的具体存在
3) 因此,面向对象解决问题应该是先考虑需要设计哪些类,再利用类创建多少个对象
需要设计哪些类,如何设计类
1) 类的设计,只关心3样东西:
- 事物名称(类名):人(Person)
- 属性:身高(height)、年龄(age)
- 行为(功能):跑(run)、打架(fight)
2) 一般名词都是类
- 坦克发射3颗炮弹轰掉了2架飞机(设计坦克类,飞机类,炮弹类)
- 小明在公车上牵着一条叼着热狗的狗(人类,公车类,狗类,热狗类)
3) 拥有相同(或者类似)属性和行为的对象都可以抽像出一个类,把具有相似,或相同属性和行为的对象,高度抽象为类,具有类名、属性、行为
4) 哪个对象最清楚这个行为,就把这个行为写到哪个对象中去。打开电脑(开机)这个行为应该是属于电脑的。
定义OC的类和创建OC的对象
要描述类分2大步骤:类的声明、类的实现(定义)。跟函数类似,函数也分声明和定义。而声明一个类又需要三大要素:类名,属性,行为
类的声明
1) 代码编写
- 定义一个Car类,拥有2个属性:轮子数、时速,1个行为:跑
- 类名\属性的命名规则:标示符的规则
- 类名的命名规范:有意义、驼峰标识、首字母必须大写
1 /* 2 类名:Car 3 属性:轮胎个数,时速 4 行为:跑 5 */ 6 //类的声明,关键字@interface后一个空格,跟一个类名,然后最后必须写@end,告诉编译器类声明完毕 7 //类的声明用来声明对象的属性(成员变量,也叫实例变量)和行为(方法) 8 //: NSObject 继承一个类(在Foundation框架里),让car类具备创建对象的能力 9 //自然要加上头文件10 #import <Foundation/Foundation.h>11 12 @interface Car : NSObject13 {//花括号里用来写对象属性,不能写方法14 //@pulic 可以让成员变量被外部指针间接的访问,15 @public16 int wheels;//轮胎个数17 int speed;//时速18 }19 20 //方法(行为)21 //三要素:方法名,参数,返回值(类似函数,分声明和实现),方法的声明在类的声明里,且不能写在花括号里。22 //方法虽然类似c的函数,但是和函数千差万别23 //只要方法是对象的方法,则声明前必须加一个减号-,这是规则!24 //oc方法里的任何数据类型都必须用小括号()括起来25 //oc方法的小括号就一个作用,就是括住数据类型的。故- (void) run();是不对的!26 - (void) run;//ok27 @end28 29 //类的实现,类似于类的声明语法,也要写@end30 @implementation Car31 //oc方法的实现,必须写在类的实现里(类的实现就是为了写方法的实现),说清楚方法里有什么32 - (void) run33 {34 NSLog(@"车子跑起来!");35 }36 @end37 38 int main()39 {40 //利用类创建对象41 //在oc,想执行一些行为,必须写上一个中括号[]42 //[行为执行者 行为名称]43 //执行Car这个类的new行为来创建对象,分配了内存,每个对象都有自己的存储空间来存储自己的成员变量等元素44 //对象里的成员变量,默认初始化为0,wheels是0,speed是045 //[Car new];//创建了一个Car类的对象,返回对象的地址(返回对象)。之后内存里存在这个对象了。46 //oc里想操作对象,只能必须用指针去间接操作!47 Car *p = [Car new];//把地址保存到指针里,p指向的是Car类型的对象(指针占8个字节)48 49 //又创建一个新对象,[Car new];每次都创建新对象50 Car *p1 = [Car new];//默认成员变量值还是051 //给p指向的对象的属性赋值52 p->wheels = 4;53 p->speed = 200;54 55 NSLog(@"车子有%d个轮子,时速是:%d km/h", p->wheels, p->speed);56 57 //[方法执行者 方法名称],想执行一个行为,用中括号58 //给p指向的对象发送一条run消息59 [p run];60 61 //程序退出,内存被回收62 return 0;63 }
@public可以让Car对象的wheels和speed属性被外界访问
加上:NSObject的目的是让Car类具备创建对象的能力,也需要Foundation头文件
main函数的代码分析、内存分析(对象在内存中有成员)
[Car new]每次都会创建出新的对象,并且返回对象的地址,那么就应该用一个指针变量保存对象的地址
Car *c = [Car new];//用一个指针变量c指向内存中的Car对象
设置车子对象的属性,跟用指向结构体的指针访问结构体属性一样,用->
c->wheels = 3;
c->speed = 300;
创建多个Car对象
分别只设置wheels、speed属性
Car *c1 = [Car new];
c1->wheels = 4;
Car *c2 = [Car new];
c2->speed = 250;
[c1 run];//对c1对象发消息,和c2无关
1个赋值给另一个,然后修改属性
Car *c1 = [Car new];
c1->wheels = 4;
c1->speed = 250;
Car *c2 = c1;
c2->wheels = 3;
[c1 run];//指针c2也指向了指针c1指向的对象,故唯一的这个对象的wheels改变为3,speed不变。
1 /* 2 人 3 类名:Person 4 属性:whight,age(oc里叫实例变量,或成员变量) 5 行为:走路(oc里叫方法) 6 */ 7 #import <Foundation/Foundation.h> 8 9 //1、类的声明10 //写成员变量,和类的方法声明11 @interface Person : NSObject12 {13 //实例变量14 @public15 int age;16 double weight;17 }18 //方法声明,对象的方法必须-开头,数据类型必须()括起来19 - (void)walk;20 21 @end22 23 //2、类的实现24 //写方法的实现25 @implementation Person26 - (void) walk27 {28 NSLog(@"%d岁, %.2f斤 的人开始走路", age, weight);//自动换行29 }30 @end31 32 int main()33 {34 Person *person = [Person new];35 person->age = 20;36 person->weight = 100.0;37 [person walk];//给指针person指向的人对象发送walk消息,告诉该对象调用属于这个对象的walk方法,这是oc的消息机制38 39 Person *person1 = [Person new];//又创建一个新的对象,用指针person1指向它,它和之前的对象完全没有关联,是开辟的新的内存空间40 person1->age = 24;41 person1->weight = 150;42 [person1 walk];43 44 person1 = person;//指针person1指向了第一个对象(person指针指向的对象)45 46 return 0;47 }
对象在内存里的变化和状态图
典型失误:oc类属性,默认是protected,外部无法访问,需要手动设置为public。还有一个典型的错误,oc千万不能想当然的和c++,java混淆,不用在类实现里再添加花括号了,直接写方法的实现即可。
和c++的不同,oc的类占据一份内存空间!在第一次使用类的时候,类被加载到内存里,即在创建对象之前,先给类分配内存空间。
注意:
1存放类的内存只被加载一次(第一次使用对象的时候加载对象属于的类)即可,且里面只存放方法的列表。
2内存里每个对象的存储空间内部默认都有一个指针叫isa指针,每个对象都有。
作用是:指向这个对象所对应的类(存放类的内存块),所以,所有的对象都共用一份方法,所以,类只被加载一次即可。成员变量每个对象都有自己的那一份内存,但是方法就共用一份内存
3调用方法的过程:
[person1 walk]; 执行:给指针person1指向的对象发送一条walk消息,去调用那个对象的walk方法,然后对象会顺着isa指针找到对应的类,去类里找方法,找到walk方法,就前往对应的代码去执行,就在walk方法里访问当前对象的成员变量age和weight(因为成员变量,每个对象都有自己的一份)
面向对象封装的好处
- 更加接近人类的思考方式
- 只需要关注对象,不需要关注步骤
oc的对象与函数的关系
- 对象成员变量作为函数参数
- 指向对象的指针作为函数参数
- 修改指针指向对象的成员
- 修改指针的指向
1 #import <Foundation/Foundation.h>//NSObject 2 @interface Car : NSObject 3 { 4 //花括号里才能写成员变量 5 @public 6 int wheels; 7 int speed; 8 } 9 - (void) run;10 @end11 12 @implementation Car13 - (void) run14 {15 NSLog(@"%d个轮子的车速是%d,跑起来了!", wheels, speed);16 }17 @end18 //oc的函数和c的函数还是比较相似的,但是方法是方法,函数是函数,要区分开来!19 //oc方法和函数是有本质区别的20 void test(int w, int s)21 {22 w = 3;23 s = 100;24 }25 26 void test1(Car *newC)27 {28 newC->wheels = 3;29 }30 31 void test2(Car *newC)32 {33 Car *c2 = [Car new];34 c2->wheels = 5;35 c2->speed = 300;36 newC = c2;//这里实际上把参数newC指针指向的地址覆盖了!!!!故不会再影响外面的对象37 newC->wheels = 6;38 }39 40 int main()41 {42 Car *c = [Car new];43 c->wheels = 4;44 c->speed = 200;45 //把刚创建的对象的成员变量值,传递给函数46 // test(c->wheels, c->speed);47 // [c run];//给对象传递run的消息48 /*49 打印结果是4个轮子的车速是200,跑起来了!50 因为test函数参数是简单的值传递,故不会改变原来的值。51 */52 53 //test1(c);54 // [c run];55 /*56 此时打印:3个轮子的车速是200,跑起来了!57 */58 59 test2(c);60 [c run];61 /*62 打印4个轮子的车速是200,跑起来了!63 */64 65 return 0;66 }
test2(c);
[c run];//打印4个轮子的车速是200,跑起来了!
这段代码在内存里是这样的:指针c指向的是函数外部的对象,开始执行方式test2,给形参newC分配内存空间,newC也指向了c指向的函数外部的对象,转到对应函数代码去执行,函数内部(局部变量)创建新的对象,用新指针c2指向,然后修改对象对应的成员变量值,关键还有一步,newC指针最后指向了函数内部创建的对象,newC = c2; newC不再指向函数外部那个对象了,故不论newC指向的对象对成员变量做如何改变,对外部对象没有影响。并且,函数执行完毕,作为局部变量,存储newC的内存消失,存储c2的内存消失。但是注意:oc的对象不会自动去回收,一旦创建,一直存在内存里,除非手动释放,或者程序执行完毕。所以,函数创建的对象不会消失。但是没有指向它的指针存在了。
objective-c学习笔记(2)