OC的三大特性
2018-09-18 本文已影响0人
VampireJune
面向对象的三大特征:
成员变量的封装、继承、多态
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;
}