面向对象的三大特征:
成员变量的封装、继承、多态
1.成员变量的封装 encapsulation : setter getter 方法
-
好处:意义在于保护、防止,代码或数据,被我们无意中破坏
- 过滤不合理的值
- 屏蔽内部的赋值过程
- 让外界不必关心内部的细节
-
setter方法- 作用:提供一个方法给外界设置成员变量值,可以在方法里面对参数进行相应过滤
- 命名规范:
1> 返回值一定是 void
2> 方法名必须以 set 开头
3> set后面跟上成员变量的名称,成员变量的首字母必须大写
4> 一定要接收一个参数,而且参数类型跟成员变量类型一致
5> 形参的名称不能跟成员变量名一样
-
getter方法- 作用:返回对象内部的成员变量
- 命名规范
1> 肯定有返回值,返回值类型肯定与成员变量类型一致
2> 方法名跟成员变量名一样
3> 不需要接收任何参数
-
成员变量- 命名规范:一定要以下划线
_开头 - 作用
1> 让成员变量和getter方法的名称区分开
2> 可以跟局部变量区分开,一看到下划线_开头的变量,一般都是成员变量
- 命名规范:一定要以下划线
@interface VampireJune : NSObject
{
// 成员变量尽量不要用 @public
// @public
int _age;
}
// setter 方法 声明
- (void)setAge:(int)age;
// getter 方法 声明
- (int)age;
@end
@implementation VampireJune
// setter 方法 实现
- (void)setAge:(int)age
{
// 这里可以对传进来的参数进行过滤
_age = age;
}
// getter 方法 实现
- (int)age
{
return _age;
}
@end
-
动态检测
- OC是在运行过程中才会检测对象有没有实现相应的方法
-
类方法都是以加号
+开头只能由
类名来调用类方法中不能访问成员变量(实例变量)可以允许
类方法和对象方法同名对象方法中可以调用类方法-
类方法的好处和使用场合- 不依赖于对象,执行效率高
- 能用
类方法,尽量用类方法 -
场合:当方法内部不需要使用到成员变量时,就可以改为类方法
@interface VampireJune : NSObject
- (void)run;
@end
@implementation VampireJune
- (void)run
{
// 执行一些代码
}
@end
int main()
{
// 调用 run 类方法
[Person run];
return 0;
}
-
self指针 : 指向了当前方法调用者-
谁调用了当前方法,
self就代表谁-
self出现在对象方法中,self就代表对象 -
self出现在类方法中,self就代表类
-
-
使用细节
- 在
对象方法利用self->成员变量名访问当前对象内部的成员变量 -
[self 方法名]可以调用其他对象方法\类方法 -
出现的地方:所有OC方法中(对象方法\类方法),不能出现在函数里
- 在
当
成员变量和局部变量同名时,采取就近原则,访问的是局部变量用
self访问成员变量,区分同名的局部变量
-
注:对象方法中 可以使用类方法 !!但不能使用 self调类方法,会造成死循环
2.继承 inheritance :类与类之间的关系
-
继承是一种语法 - 单继承
- 基本上所有
类的根类都是NSObject -
@interface类名 后面的冒号:就是继承[英文冒号] - 父类/超类:
superclass - 子类:
subclass/subclasses
@interface VampireJune : NSObject // 继承自 NSObject 类
@end
-
好处
- 抽取重复代码
- 建立了
类之间的关系 -
子类可以拥有父类中的所有成员变量和方法
-
坏处
- 耦合性太强 [当
父类没有了,子类就不能用了]
- 耦合性太强 [当
注意点
1.重写:子类重写实现父类中的某个方法,覆盖父类以前的做法
2.父类必须声明在子类的前面
3.子类不能拥有和父类相同名称的成员变量
4.调用某个方法时,优先去当前类中找,如果找不到,去父类中找
5.类方法同 4. 理-
使用场合
- 当两个
类拥有相同属性和方法时,就可以将相同的东西抽取到一个父类中 - 当
A类拥有B类中的部分属性和方法时,可以考虑让B类继承A类 - 当
B是A时,即A``B是同一类型物体,如,A``B均是人,或均是动物 - 继承:B 是 A
- 组合:B 拥有 A
- 当两个
- 继承 :
@interface A : NSObject
{
int _age;
}
@end
@interface B : A
{
int _weight;
}
@end
- 组合 :
@interface A : NSObject
{
int _age;
}
@end
@interface B : NSObject
{
A *_a;
int _weight;
}
@end
-
super- 作用
- 直接调用
父类的某个方法 -
super处在对象方法中,就会调用父类的对象方法 -
super处在类方法中,就会调用父类的类方法
- 直接调用
- 作用
-
使用场合
-
子类重写父类的方法时,保留父类的方法,不完全覆盖
-
3.多态 Polymorphism :多种形态
- 没有
继承就没有多态 -
代码的体现:父类类型的指针指向子类对象
@interface Animal : NSObject
- (void)eat;
@end
@implementation Animal
- (void)eat
{
NSLog(@"------%@------吃东西------",self);
}
@interface Dog : Animal
- (void)run; // Dog 特有的方法,父类 Animal 没有,验证多态局限性
@end
@implementation Dog
- (void)run
{
NSLog(@"------Dog------跑起来------");
}
- (void)eat
{
NSLog(@"------Dog------吃东西------");
}
@end
int main()
{
Dog *d = [Dog new]; // Dog 类型
// 多态 :`父类指针`指向`子类对象`
Animal *a = [Dog new];
// 调用方法时,会检测对象的真实类型(这就是动态检测)
[a eat];
// 这也是多态
NSObject *n = [Dog new];
NSObject *n2 = [Animal new];
// 这样是不合理的,OC是弱语法,虽然编译器不报错,只是警告,但是也不要这么写
// Dog *d = [Animal new];
return 0;
}
- 好处
- 如果
函数\方法参数中使用的是父类类型,可以传入父类、子类对象
- 如果
@interface Cat : Animal
@end
@implementation Cat
- (void)eat
{
NSLog(@"------Cat------吃东西------");
}
@end
// 专门用来喂动物的函数
void feed (Animal *a)
{ // 如果参数中使用的是父类类型,可以传入父类、子类对象
[a eat];
} // 只写一个方法,节省了写多个方法的代码
int main()
{
Animal *a = [Animal new];
feed(a);
Dog *d = [Dog new];
feed(d);
Cat *c = [Cat new];
feed(c);
return 0;
}
- 局限性
-
父类类型的变量 不能 直接调用子类特有的方法 - 必须
强转为子类类型变量后,才能直接调用子类特有的方法
-
int main()
{
// 使用多态 调用 `run` 方法
Animal *a = [Dog new];
// [a run]; // 从语法角度上,这样不合理
// 编译器肯定先去 `Animal` 里找 `run` 方法
// 编译器找不到 `run` 方法,只报出一个警告
// 因为调用对象方法是动态绑定,动态检测对象的真实类型
// 然后就会去 `Dog` 里面找 `run` 方法,还是运行成功
// 这样写不好,可以强制转换,将 `a` 转换为 `Dog *` 类型的变量
Dog *dd = (Dog *)a;
[dd run];
Dog *d = [Dog new];
[d run];
return 0;
}