iOS基础 - oc的三大特性
封装、继承、多态
oc是面向对象的程序设计语言,而面向对象的三大特征:封装、继承、多态
。
封装
没有绝对的封装,只有需要的api。我们在开发过程中都是为了需求而开发,要达成某个样子的效果。话不多说直接上代码。
//.h
@interface PersonViewModel : NSObject
/// 初始化
- (instancetype)initWithPersonModel:(PersonModel* )personModel;
@property (readonly,nonatomic,assign)NSInteger age;
@property (readonly,nonatomic,copy)NSString* name;
@end
//.m
@interface PersonViewModel()
- (instancetype)initWithPersonModel:(PersonModel* )personModel;
@property (nonatomic,assign,readwrite)NSInteger age;
@property (nonatomic,copy,readwrite)NSString* name;
@end
@implementation PersonViewModel
- (instancetype)initWithPersonModel:(PersonModel* )personModel
{
self = [super init];
if (self) {
self.age = personModel.age;
self.name = personModel.name;
}
return self;
}
@end
在PersonViewModel
中 我们只提供了类的初始化方法和两个只读的属性,这样我们就不能再对当前的对象进行修改数据,只是获取数据的展示。而在某些场景中我们需要对数据进行修改,这种方式显然就适合,所以根据具体的需求来封装是很有必要的。其实这里已经引出了一个思想,把不必要的展示内容给隐藏起来。
封装的作用:
- 让使用者只能通过事先预定好的方法或属性来获取数据,从而可以在该方法中加入控制逻辑,限制对属性和成员变量的不合理访问。
- 可进行数据检查,从而有利于保证对象的完整性。
- 便于修改,提高代码的可维护性。
为了实现良好的封装,应该从下面两个方面考虑:
- 将对象的成员变量和实现细节都隐藏起来,不允许外部访问。
- 把方法暴露出来,让方法控制对成员变量进行安全的访问和操作。
访问控制符
@private @package @protected @public
它们的访问控制级别由小到大依次如下图:

- @private:【当前类访问权限】,只能在当前类内部被访问。用于彻底隐藏成员变量。类的实现部分定义的成员变量相当于默认用这种访问权限。
- @package:【与映像访问权限相同】用于部分隐藏成员变量。如果类的成员变量使用@package 访问控制符来限制,则这个成员可以在当前类及当前类实现的同一个映像的任意地方访问。
- @protected:【子类访问权限】用于部分隐藏成员变量。如果类的成员变量使用@protected 访问控制符来限制,则这个成员可以在当前类,当前类的子类的任意地方访问。在类的接口部分定义的成员变量默认使用这种访问权限。
-
@public:【公共访问权限】,用它限制的类的成员可以在任意地方访问,不管是否处于同一映像中,也不管是否具有继承关系。
综上,我们用图表进行下总结:
访问控制级别
以及代理中的@required
@optional
@protocol PersonDelegate <NSObject>
@required
-(void)eat;
@optional
-(void)lookBook;
@end
继承
特点
单继承
子类可以拥有父类中的所有成员变量和方法
子类可以重写父类的方法
调用某个方法时,优先去当前类中找,如果找不到,去父类中找
使用场合
两个类拥有相同属性和方法时,就可以将相同的东西抽取到一个父类中
当A类拥有B类中的部分属性和方法时,可以考虑让B类继承A类
当 B 是 A 时,即AB是同一类型物体,如,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
多态
oc 指针类型的变量有两个,一个是编译时的类型,一个是运行时的类型。编译时的类型由声明该变量的类型觉得,运行时的类型由实际赋给改变量的对象决订,如果运行时的类型和编译时的类型不一致就可能出现多态。
int main(int argc, char * argv[]) {
BaoMa *baoma = [BaoMa new];//BaoMa类型
// 多态 :`父类指针`指向`子类对象
Bar * bar = [BaoMa new];
// 调用方法时,会检测对象的真实类型(这就是动态检测
// [Bar name];
[bar safeNum];
// 多态 :`父类指针`指向`子类对象
NSObject *obj1 = [BaoMa new];
NSObject *obj2 = [Bar new];
// 这样是不合理的,OC是弱语法,虽然编译器不报错,只是警告,但是也不要这么写
BaoMa *baoma1 = [Bar new];
//动态解析时报错,找不到这个方法
[baoma1 name];
}
上面 写的很清楚bar
实际上是BaoMa
类型的对象,但是却不能调用BaoMa
类的方法,可以通过动态解析或者强转类型进行调用。
即 父类类型的变量 不能 直接调用子类特有的方法
必须强转为子类类型变量后,才能直接调用子类特有的方法。
或者动态解析调用子类方法。
我们再来看一看swift:
protocol peopleHome{
func safeNum() -> Int
}
class Bar:peopleHome{
func safeNum() -> Int {
return 45
}
}
class Car:peopleHome{
func safeNum() -> Int {
return 12
}
}
class Plane:peopleHome{
func safeNum() -> Int {
return 200
}
}
let bar = Bar()
let car = Car()
let plane = Plane()
var transLate:[peopleHome] = [bar,car,plane]
for i in transLate{
if i is Any{
print("any")
}
if i is peopleHome{
print("peopleHome")
i.safeNum()
}
if i is Bar{
print("Bar")
i.safeNum()
}
if i is Car{
print("Car")
i.safeNum()
}
if i is Plane{
print("Plane")
i.safeNum()
}
}

此时此刻
transLate
是协议数组[peopleHome]
类型 ,而实际的数组内容是Bar
Car
Palne
类型。也产生了多态。
多态 的好处?
void run (Bar *bar){
// 如果参数中使用的是父类类型,可以传入父类、子类对象
if ([bar isMemberOfClass:[BaoMa class]]){
NSLog(@"我是BaoMa");
}
[bar safeNum];
}
int main(int argc, char * argv[]) {
Bar *bar = [Bar new];
run(bar);
BaoMa * baoma = [BaoMa new];
run(baoma);
return 0;
}

函数/方法
参数中使用的是父类类型,可以传入父类、子类对象。
如有补充或交流请加微信
