一、封装(set方法和get方法)
-
封装的好处:
过滤掉不合理的值,屏蔽内部的赋值细节,让外界不比关心内部的细节。
-
set方法:
1.作用:提供一个方法给外界设置成员变量的值
2.命名规范: 1> 方法名必须是set开头 2> set后面跟上成员变量的名称,并且成员变量的首字母必须是大写 3> 返回值一定是void 4> 一定要接受一个参数,而且参数类型跟成员变量的类型一致 5> 形参名称不能和成员变量名一样
例:
- (void)setAge : (int) newAge; // 方法声明- (void)setAge : (int) newAge // 方法实现{ if (newAge <= 0){ newAge = 1; // 对传入的参数进行过滤 } age = newAge;}
-
get方法:
1.作用:返回对象内部的成员变量值
2.命名规范: 1> 肯定有返回值,并且返回值类型肯定和成员变量类型一致 2> 方法名和成员变量名一样 3> 不需要接受任何参数例:
- (int) age; // 方法的声明- (int) age // 方法的实现{ return age; }
-
成员变量的命名规范
成员变量的命名规范:一定要以下划线“_”开头;
作用:1.让成员变量和get方法的名称区分开
2.可以跟局部变量区分开,一看到下划线开头的变量,一般都是成员变量
代码练习:
#import@interface Student : NSObject{ // 成员变量尽量不要用@public保证数据的安全性,没有@public就不能通过 对象->成员变量 来赋值(访问),这是可以提供一个set方法给外界设置成员变量的值(虽然没有了@public,但是在对象方法内还是能直接访问成员变量的) // @public int age;}- (void)setAge:(int)newAge;- (int)age;- (void)study;@end@implementation Student// set方法的实现- (void)setAge:(int)newAge{ // 对传进来的参数进行相应的过滤 if(newAge <= 0) { newAge = 1; } age = newAge;}// get方法的实现- (int)age{ return age;}- (void)study{ NSLog(@"%d岁的学生在学习", age); // 这里的使用age即使在声明成员变量时没有写@public也可以直接访问(对象方法内部可以直接访问成员变量)}@endint main(){ Student *stu = [Student new]; // 通过调用set方法对对象内部的成员变量进行赋值 [stu setAge:-10]; [stu study]; // 由于没有了@public这是调用get方法获取对象内部的成员变量值 NSLog(@"这个学生的年龄是%d", [stu age]); return 0;}
二、继承
1.继承的好处:
-
抽取重复代码
-
建立了类与类之间的关系
-
子类拥有了父类中的所有成员变量和方法
2.继承的坏处:
-
耦合性太强,子类与父类必须相互依存(如果将父类的代码 删除了,子类也不能正常使用);
3.继承注意点:
-
基本所有类的根类是NSObject;
-
父类的声明必须写在子类前面,实现无所谓;
-
不允许子类有和父类相同的成员变量;
-
调用某个方法时,优先在当前类中找,找不到就会去父类中找(内存分析见下图);
-
允许子类中有和父类相同的方法,即:重写(子类重新实现父类中的某个方法,这是在子类中调用该方法就会优先执行子类中重新实现的方法,覆盖父类以前的方法);
4.继承的使用场合:
-
当两个类拥有相同属性和方法时,就可以将相同的东西抽取到一个父类中;
-
当A类中完全拥有B类中的部分属性和方法时,可以考虑让B类继承A类;
-
注意:1.并不是只要有相同的属性和方法就可以使用继承;
2.使用继承时要考虑这两个类是否存在逻辑上的关系;
3.如果不存在逻辑关系,就用组合;
代码一:
#import/*********Animal类的声明**********/@interface Animal : NSObject{ int _age; double _weight;}- (void)setAge:(int)age;- (int)age;@end/*********Animal类的实现**********/@implementation Animal- (void)setAge:(int)age{ _age = age;}- (int)age{ return _age;}@end// : Animal 代表Dog类继承了Animal类,那么Dog类中就拥有了Animal类中的所有成员变量及方法// Animal类就是Dog类的父类// Dog类就是Animal的子类/*********Dog类的声明**********/@interface Dog : Animal@end/*********Dog类的实现**********/@implementation Dog@endint main(){ Dog *d = [Dog new]; [d setAge:10]; NSLog(@"age=%d", [d age]); return 0;}
代码二:
#import@interface Person : NSObject{ int _age;}- (void)run;@end@implementation Person- (void)run{ NSLog(@"person---跑");}@end// 注意点1:子类的声明必须写在父类的声明后面@interface Student :Person{ int _no;// 注意点2:不允许在子类中有和父类相同的成员变量 // int _age; // 报错:重复定义_age(error: duplicate member '_age')}// - (void)run; // 这里可以不写,因为在其父类中已经有声明,如果子类实现中有该方法的实现,就是重写(子类重新实现父类中的某个方法)@end@implementation Student// 重写:子类重新实现父类中的某个方法,这是在子类中调用该方法就会优先执行子类中重新实现的方法,覆盖父类以前的方法- (void)run{ NSLog(@"student---跑");}@endint main(){ Student *s = [Student new]; [s run]; // 会现在子类中找run方法,找到了就优先执行当前类中run方法,没有找到就会去其父类中找 return 0;}
代码三:
#import@interface Score : NSObject{ int _cScore; int _ocScore;}@end@implementation Score@end// 继承:xx 是 xxx// 组合:xx 拥有 xxx@interface Student :NSObject{ int _no; int _age; Score *_score; // 这里用的就是组合}@end@implementation Student@endint main(){ return 0;}
三、多态(父类指针指向子类对象)
1.代码体现:父类类型的指针指向子类对象;
2.好处:如果函数、方法形参中使用的是父类类型,就可以传入父类、子类对象;
3.局限性:父类类型的变量不能直接调用子类特有的方法,必须强转为子类类型后才能直接调用子类特有方法
4.代码练习:
#import@interface Animal : NSObject- (void)eat;@end@implementation Animal- (void)eat{ NSLog(@"Animal---吃东西");}@end@interface Dog : Animal- (void)run;@end@implementation Dog- (void)eat{ NSLog(@"Dog---吃东西");}- (void)run{ NSLog(@"狗跑起来了");}@end@interface Cat : Animal@end@implementation Cat- (void)eat{ NSLog(@"Cat---吃东西");}@end// 喂狗void feed(Dog *d){ [d eat];}// 喂猫void feed2(Cat *c){ [c eat];}// 由于喂狗和喂猫的代码基本一致,所以考虑将其抽取到一个函数内// 多态的好处:如果形参中使用的是父类类型,就可以传入父类、子类对象void feed3(Animal *a){ [a eat];}int main(){ /***************1.多态的基本使用******************/ // 多态:父类指针指向子类对象 Animal *a = [Dog new]; // 调用方法时会动态检测对象的真实类型,这里对象a的真实类型是Dog [a eat]; // 输出:Dog---吃东西 /***************2.OC的弱语法******************/ /* 注意点: OC的弱语法:这里仅仅是一个警告(warning: incompatible pointer types initializing 'Dog *' with an expression of type 'Animal *' [-Wincompatible-pointer-types]) */ // Dog *d = [Animal new]; // 虽然仅仅是一个警告,但是在意思上不合理(动物是狗) // [d eat]; // 输出:Animal---吃东西 /***************3.多态的好处******************/ // 多态的好处:如果形参中使用的是父类类型,就可以传入父类、子类对象 Dog *d2 = [Dog new]; feed(d2); Cat *c2 = [Cat new]; feed2(c2); Dog *d3 = [Dog new]; feed3(d3); Cat *c3 = [Cat new]; feed3(c3); /***************4.多态的局限性******************/ Dog *dd = [Dog new]; // Dog类型 Animal *aa = [Dog new]; // Animal类型 // OC的弱语法: warning: 'Animal' may not respond to 'run' // 对于编译器来讲,编译器认为aa的类型是Animal,这里[aa run]就会去Animal中找是否有run的具体实现,没有找到就会警告,但是 还是会运行成功,因为在运行是会动态检测aa的真实类型(Dog),Dog类中有run方法的具体实现,就能正常运行 // 多态的局限性:父类类型的变量不能用来调用子类特有的方法 // [aa run]; // 对于上面的问题,可以将aa强制转换成Dog类型,编译器就不会警告 Dog *dd = (Dog *)aa; // 将aa强制转换成Dog *类型 [dd run]; return 0;}