【Objective-C】面向对象—三大特性

时间:2022-02-07 19:48:24

一、封装

1. 面向对象的三大特性:封装(成员变量)、继承和多态

    在OC语言中,使用@interface@implementation来处理类。(图片来源于官网)

【Objective-C】面向对象—三大特性

  @interface就好像暴露在外面的时钟表面,像外界提供展示以及接口。@implementation就好像隐藏在时钟内部的构造实现,把具体的实现封装了起来。

2. 封装:隐藏内部实现,稳定外部接口.

    封装就是定义类,定义属性,定义方法;

3. Set方法

    在开发过程中,考虑到安全性要求,我们一般不在成员变量名前面使用@public@protected等关键字修饰,而是使用Set方法来为对象提供成员变量的值。在set方法的内部也可以对一些不合理的赋值进行筛选过滤。

    Set方法的作用:为外界提供一个设置成员变量值的方法

      命名规范:

      1)方法名必须以set开头

      2)Set后面跟上成员变量的名称,首字母大写

      3)返回值一定是void

      4)一定要接收一个参数,而且参数类型需要和成员变量的类型一致;

      5)形参名不能和成员变量名一样(苹果官方推荐成员变量名前加_以示区分);

      Set方法的好处:(提供set 以及get 给别人使用就行)

        1)不让数据暴露在外,保证了数据的安全性;

        2)对设置的数据进行过滤 ; 

#import <Foundation/Foundation.h>

// 声明
@interface Person : NSObject {
//
int _age;
}

//
- (void)setAge:(int)age;
- (void)print;

@end


// 实现

@implementation Person

//
- (void)setAge:(int)age {
_age = age;
}

- (void)print {
NSLog(@"age = %i", _age);
}

@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person *p = [[Person new] init];
[p setAge:80];
[p print];

}
return 0;
}
4. Get方法

    Get方法的作用:为调用者返回对象内部的成员变量

    命名规范:

     1)一定有返回值,返回值的类型和成员变量的类型一致

     2)方法名和成员变量名一样

     3)不需要接收任何参数

#import <Foundation/Foundation.h>

// 声明
@interface Person : NSObject {
//
int _age;
}

//
- (void)setAge:(int)age;
- (void)print;

// get方法
- (int)age;

@end


// 实现

@implementation Person

//
- (void)setAge:(int)age {
_age = age;
}

- (void)print {
NSLog(@"age = %i", _age);
}

// get
- (int)age {
return _age;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
Person *p = [[Person new] init];
[p setAge:80];
[p print];

//
NSLog(@"利用get方法返回的值是: %i", [p age]);

}
return 0;
}
    注意1: 在实际的开发中,不一定 set get 方法都会提供,如果内部的成员变量比如学生的学号这样的数据只允许外界读取,但是不允许修改的情况,则通常只提供 get 方法而不提供 set 方法。

    注意2: 成员变量名的命名以下划线开头,get方法名不需要带下划线,使用下划线开头有两个好处:(1)与get方法的方法名区分开来;(2)可以和一些其他的局部变量区分开来,下划线开头的变量,通常都是类的成员变量。

5. Self关键字

    Self是一个指针,谁调用了当前方法,self就指向谁

  【出现在对象方法中,就代表着当前对象,出现在类方法中,就代表着当前类】

    Self的用途:

      (1) 可以利用self->成员变量名访问当前对象内部的成员变量(仅在对象方法中)

      (2) [self 方法名];可以调用其他的对象方法或者是类方法

  关于self的关键字使用:http://blog.csdn.net/haojie2014/article/details/46908955

6.补充:OC弱语法

注意的是:Oc语言是一门弱语法的语言,编译的时候不会报错,所以这就要求我们开发过程中一定要按照既定的规范进行写代码。

  1. oc在运行过程中才会检测对象有没有实现该相应的方法(动态监测),而即使没有写方法的实现代码,在编译、连接过程中只会显示警告,运行时会崩溃,如果在手机上运行运行遮掩的代码,运行过程中很可能就会造成闪退。

  经典错误:不能识别消息发送给对象(运行时会监测方法有没有实现),会使程序运行时自动崩溃(闪退)

-[Person test]: unrecognized selector sent to instance 0x7f9738403420

  2. 如果对象调用的方法只有声明没有实现,编译能通过(有警告:warning: method definition for ‘test‘ not found [-Wincomplete-implementation]),但是能连接成功,运行时还是会导致程序崩溃

  3. 如果对象调用的方法只有实现,没有声明,编译能通过,能连接成功,也能顺利运行,但是不采用这样的写法

  4. 只有类的声明没有类的实现也可以顺利运行,但是不采用这样的写法

#import <Foundation/Foundation.h>

@interface Person : NSObject {
int _age;
}

- (void)test;

@end


@implementation Person
// 没有实现test方法

@end

int main(int argc, const char *argv[]) {
@autoreleasepool {
Person *p = [Person new];

[p test];

}
return 0;

}
结果:

【Objective-C】面向对象—三大特性

二、 继承

1. 程序的世界与人类的“对象”世界在思想上没有什么区别,子类继承了父类,其实就拥有了父类的方法和属性(成员变量),继承及时代码优化公共部分交给父类。

    子类可以直接复用父类中的成员。子类继承父类所有方法的声明和实现,非私有的实例变量以及协议,继承是要在 .h 文件中声明一下。继承具有单根性和传递性

拓展:

   Objective-C不支持多继承,这与c#, java一致,但与c++有点区别。也就是说每个类(除了根类NSObjec)外有且只能有一个基类,但是可以有另个或者若干个子类,这种关系正是树的结构特征,所以oc类呈现出的是树形层次的结构。

2. 继承的好处

    1). 抽取出了重复的代码;

    2). 建立了类和类之间的联系;

    缺点

    耦合性太强;

3. OC中的继承

@interface Animal:NSObject

//动物里继承了NSObject,获得NSObject类的方法;

@end

@interface Dog :Animal

//dog类继承Animal类

@end

注意:OC语言是单继承语言。在oc语言中,基本上所有类的根类都是NSObject类。

4. 继承的使用注意

  1). 编译器从上往下执行,所以在子类前面至少应该要有父类的声明;

  2). oc中不允许子类和父类拥有相同名称的成员变量名;

  3). oc中的子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部寻找,如果没有则一层一层往上找;

提示:重写就是子类重新实现了父类中的某个方法,覆盖了父类以前的实现。

示意图:

  【Objective-C】面向对象—三大特性

其实就是创建一个Student *s = [[Student alloc] init];

此时会把Student类和这个类的父类加载紧内存;

提示:每个类中都有一个super class指针,该指针指向自己的父类。对象中有一个isa指针,该指针指向调用该对象的类。

5. 继承与组合

继承的适用场合:

  1)当两个类拥有相同的属性和方法时,就可以将相同的属性和方法抽取到一个父类中。

  2)当A类完全拥有B类中的部分属性和方法时,可以考虑让B类继承A类(考虑),在这种情况下,也可以考虑使用组合。

继承: A 是 B;

组合:A 中有 B; 就是让B这个类作为类A的属性;

6. 关键字Super

  Super关键字,在子类中重写方法时,可以让调用者跳过这一层而调用父类中的方法。

-(void)dealloc {
//
[super dealloc];
}
  作用:

   1)直接调用父类的某一个方法;

   2)Super处在对象方法中,那么就会调用父类的对象方法;super处于类方法中,那么就会调用父类的类方法;

使用场景: 子类重写父类方法时,想保留父类的一些行为


三、 多态

1. 多态在代码中的体现,就是多种形态,必须要有继承,没有继承就没有多态;

2. 在使用多态时,会进行动态监测,以调用真实的对象方法;

3. 多态在代码中的体现即父类指针指向子类对象;

4. 不同对象对同一消息的不同相应,子类可以重写父类的方法,多态就是允许方法重名 参数或返回值可以使父类行传入或返回。  

#pragma mark - polymorphism
void polymorphism() {
// Animal
Animal *a = [[Animal alloc] init];
[a eat];

// Dog
Dog *d = [[Dog alloc] init];
[d eat];

// Guess what output
// The parent class pointer to the child object
Animal *a1 = [[Dog alloc] init];
NSLog(@"这里使用Dog初始化Animal!");
[a1 eat];
// 动态监测,调用方法时候会检测对象的真是类型

// Guess what output again
// Dog *d1 = [[Animal alloc] init];
// NSLog(@"这里使用Animal初始化Dog");
// [d1 eat];

}
5. 多态的局限性:父类类型的指针变量不能直接调用子类特有的方法
Animal *a=[[Dog alloc] init];[a run]; //在Animal类中没有run方法,这里调用了狗对象的方法。解决方法:可以将a强制转换为Dog*类型的变量,如下:Dog *d=(Dog *)a;//使用强制转换,这里a和d指向的是同一个狗对象
6. 总结

  1)没有继承就没有多态;

  2)代码的体现,父类类型的指针指向子类对象;

  3)好处:如果函数方法参数中使用的是父类类型,则可以传入父类和子类对象,而不用再去定义多个函数来和响应的类进行匹配;

  4)局限性:父类类型的变量不能直接访问子类特有的方法,如果一定要调用,则必须强制转化为子类特有的方法;


关于面向对象的三特性还可以阅读文章:http://blog.csdn.net/haojie2014/article/details/48064563(了解面向对象三大特性)