ObjectiveC-09-OOP面向对象程序设计-继承

2024-04-04 20:28

本文主要是介绍ObjectiveC-09-OOP面向对象程序设计-继承,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

继承是所有高级语言都实现的一种特征,比如java、python甚至现在的js也有类似继承的写法。在表层目的是减少重复代码,深层目的是为了匹配业务与程序间的映射关系。

先来看下OOP设计思起中的继承的关系表示
在这里插入图片描述

再结合实例来看下完整的关系表示
在这里插入图片描述
一个简单的例子示例:

@interface SubClass: NSObject{ParentClass *par;
}
- (int) side;
- (int) area;

继承概述

子类会继承父类的所有功能和属性,在Objc中默认所有类的超类都是NSObject。比如初始化对象时常用的alloc和init方法就是在NSObject中定义的。继承的语法格式为:

@interface InterfaceName: SupperInterfaceName

NSObject类中提供了很多与继承相关的工具方法,比如:isKindOfClass、isMemberOfClass、performSelector等,这些方法的工作原理也比较简单,就是在子类中拥有一个父类的指针。函数方法被调用时先从子类中寻找,如果没有的话再去父类中寻找相应的方法。

super:指向父类的指针;
self:指向当前类的指针;

在Objc语言中不支持多继承,多继承可以用分类来实现。

@interface ParentClassName:NSObject
...
@end@interface ChildClassName:Fraction
...
@end

继承示例(一)

下图是一个简单的类图,定义一个抽象的图形父类,然后具像化两个标准的图形类:圆和正方形。
在这里插入图片描述

定义父类-Rectangle

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface Rectangle : NSObject{int size;int length;
}//公共方法
- (void) setSize:(int)size;
- (void) setLength:(int)length;//由各子类实现的个性化方法
- (void) print;@endNS_ASSUME_NONNULL_END//---------实现代码
#import "Rectangle.h"@implementation Rectangle
- (void) setSize:(int)si{size = si;
}
- (void) setLength:(int)le{length = le;
}
@end

定义子类1-Square

#import "Rectangle.h"NS_ASSUME_NONNULL_BEGIN@interface Square : Rectangle@endNS_ASSUME_NONNULL_END#import "Square.h"@implementation Square- (void) print{NSLog(@"This is Square Object, size=%d and length=%d", size, length);
}@end

定义子类2-Circle

#import "Rectangle.h"NS_ASSUME_NONNULL_BEGIN@interface Circle : Rectangle@endNS_ASSUME_NONNULL_END#import "Circle.h"@implementation Circle- (void) print{NSLog(@"This is Circe Object, size=%d and length=%d", size, length);
}@end

测试子类

#import <Foundation/Foundation.h>
#import "Square.h"
#import "Circle.h"
#import "Rectangle.h"int main(int argc, const char * argv[]) {@autoreleasepool {/* 注意上面两种调用方法的不同,通常建议的是第二种调用方式*/Square *square = [[Square alloc] init];[square setSize:1];[square setLength:2];[square print];Rectangle *s = [[Square alloc] init];[s setSize:5];[s setLength:6];[s print];Circle *circle = [[Circle alloc] init];[circle setSize:3];[circle setLength:4];[circle print];}return 0;
}

其程序执行过程如下:
在这里插入图片描述

扩展

扩展父类功能

在子类中可以扩展父类没有的属于特定子类特有的属性和方法,比如再新建一个名为RectExtProperty的类,其代码实现如下:

  • 子类定义
@interface RectExtProperty : Rectangle{
@privateint radius;  //扩展的属性,如果用private来修饰的话意味着不可再被继承
}- (void) setRadius;  //扩展的方法@endNS_ASSUME_NONNULL_END
  • 子类实现
#import "RectExtProperty.h"@implementation RectExtProperty
- (void) print{NSLog(@"This is Square Object, size=%d and length=%d", size, length);NSLog(@"radius=%d", radius);
}- (void) setRadius{radius = size * length;
}
@end
  • 测试代码
        RectExtProperty *rep = [[RectExtProperty alloc] init];[rep setSize:3];[rep setLength:4];[rep setRadius];rep.print;/*~~2024-03-26 22:58:45.575058+0800 inheritanceDemo[50224:5458478] This is Square Object, size=3 and length=4
2024-03-26 22:58:45.575079+0800 inheritanceDemo[50224:5458478] radius=12
*/

覆盖父类功能

**一般只有方法覆盖,不会存在属性覆盖的情况发生,如果属性覆盖则会报重名异常。**此种情况一般适用于当父类有了默认实现时,子类想扩展默认实现的功能时才会使用,还是拿上面的RectExtProperty为例。

在Rectangle类中添加默认实现,此时发现输出不变,因为父类的方法被子类覆盖掉了。

- (void) print{NSLog(@"I am a parentClass");
}

在RectExtProperty子类中添加以下代码,则就会打印上面的 I am a parentClass 这一行代码。注意这里的super用法,它表示指向父类的指针。

- (void) print{[super print]; //新增的代码NSLog(@"This is Square Object, size=%d and length=%d", size, length);NSLog(@"radius=%d", radius);
}

其调用链路如下:
在这里插入图片描述

继承示例(二)

这个例子和上面没啥区别,只不过是用一些注解简化了代码。

定义父类-Rectangle

#import <Foundation/Foundation.h>@interface Rectangle : NSObject{int flag;
}@property int width, height;- (int) area;
- (int) perimeter;
- (void) setWidth: (int)w andHeight: (int)h;@end//----------父类实现---------------------
#import "Rectangle.h"@implementation Rectangle- (void) setWidth: (int)w andHeight: (int)h{width = w;height = h;
}- (int) area{return width * height;
}- (int) perimeter{return (width+height)*2;
}@end

定义子类-Square

#import "Rectangle.h"@interface Square : Rectangle- (void) setSide: (int)s;
- (int) side;@end//------------子类实现-------------------
#import "Square.h"@implementation Square: Rectangle- (void) setSide: (int)s{[self setWidth:s andHeight:s];
}- (int) side{return self.width; //用这种方法可以取得父类的私有变量。
}@end

测试子类

#import "Square.h"int main(int argc, const char * argv[]) {@autoreleasepool {Square *square = [[Square alloc] init];[square setWidth:5];NSLog(@"This is = %i", square.perimeter);}return 0;
}

类定义常用的注解与注意事项

@class 类依赖优化

@class会创建一个前向引用,它是@import的替代品,它的作用:

  1. 可以缩短编译时间,编译效率会高于@import。比如有100个.m文件都导入了同一个.h文件,如果.h修改了则这100个.m文件都需要重新进行编译;
  2. 解决循环依赖的问题,比如A依赖B,B又依赖了A,如果使用@import那么会出现编译错误,使用@class则没有问题。

示例代码如下:

#import <Foundation/Foundation.h>
#improt "XYPoint.h" //待替换@implementation Rectangle{XYPoint *origin;
}

上述代码可替换为如下代码,@class XYPoint; 代码的意思就是XYPoint是一个类,程序只会通过指针来引用它,真正运行时再从指针地址取到实际的值。

#import <Foundation/Foundation.h>
@class XYPoint;@interface Rectangle:NSObject
@property int width, height;-(XYPoint *) origin;
-(void) setOrigin : (XYPoint *) pt;

这两个标签的区别,简单理解就好比是@import是复制代码到当前文件中,而@class是引用代码并不复制。

@selector SEL函数变量

可以为一个函数生成一个SEL类型的值,类似于一种安全检查,SEL可做为参数使用,也可用于对象是否拥有特定函数前的逻辑判断。比如如下代码,其实就是判断类Fraction是否实现了接口定义的setTo方法。

SEL action =  @selector(setTo:over:); //为setTo方法生成一个SEL
Boolean boolean = [Fraction instanceMethodForSelector: @selector(setTo:over:)]; //验证类是否响应了setTo方法id obj;
[obj performSelector: setTo:over:] //调用setTo:over:方法

综合示例

Square *mySquare = [[Square alloc] init];
if ( [mySquare isMemberOfClass:[Square class]] == YES ){NSLog(@" yes");
}if ( [mySquare isKindOfClass:[Square class]] == YES ){NSLog(@" yes");
}if ( [mySquare respondsToSelector :@selector(setSide:)] == YES ){NSLog(@" yes");
}if ( [Square instancesRespondToSelector :@selector(setSide:)] == YES ){NSLog(@" yes");
}2023-12-20 21:09:50.751682+0800 FractionTest[35384:5245425]  yes
2023-12-20 21:09:50.751721+0800 FractionTest[35384:5245425]  yes
2023-12-20 21:09:50.751749+0800 FractionTest[35384:5245425]  yes
2023-12-20 21:09:50.751776+0800 FractionTest[35384:5245425]  yes

关于抽象类

在ObjectiveC并没有实现介于实现和接口中间状态的抽象类型(比如java中的抽象类)。但在ObjectiveC中也有抽象的概念,只不过它的抽象类有点特殊,ObjectiveC语言提供的抽象更像是一种中间层代理。比如Foundation中提供的 NSNumber 就是一个处理数字的抽象类,它有很多个子类但都是私有的。当使用 NSNumber 时系统会自运选择一个合适的子类来创建,这种处理方式在ObjC中有一个专门的名称,称为簇(cluster)。

ObjectiveC中的抽象类似于一个工厂设计模式,对比下传统OOP中抽象和ObjectiveC中的抽象设计可能会更清楚。
在这里插入图片描述
…end

下一节内容,笔者会详细讲述下ObjectiveC OOP编程中的分类的使用,分类是一个非常重要的特性,有了分类就可以很容易实现模块化开发。

这篇关于ObjectiveC-09-OOP面向对象程序设计-继承的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/876770

相关文章

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

Java第二阶段---09类和对象---第三节 构造方法

第三节 构造方法 1.概念 构造方法是一种特殊的方法,主要用于创建对象以及完成对象的属性初始化操作。构造方法不能被对象调用。 2.语法 //[]中内容可有可无 访问修饰符 类名([参数列表]){ } 3.示例 public class Car {     //车特征(属性)     public String name;//车名   可以直接拿来用 说明它有初始值     pu

C语言程序设计(数据类型、运算符与表达式)

一、C的数据类型 C语言提供的数据类型: 二、常量和变量 2.1常量和符号常量 在程序运行过程中,其值不能被改变的量称为常量。 常量区分为不同的类型: 程序中用#define(预处理器指令)命令行定义变量将代表常量,用一个标识符代表一个常量,称为符合常量。 2.2变量 变量代表内存中具有特定属性的一个存储单元,用来存放数据,在程序运行期间,这些值是可以 改变的。 变

C语言程序设计(选择结构程序设计)

一、关系运算符和关系表达式 1.1关系运算符及其优先次序 ①<(小于) ②<=(小于或等于) ③>(大于) ④>=(大于或等于 ) ⑤==(等于) ⑥!=(不等于) 说明: 前4个优先级相同,后2个优先级相同,关系运算符的优先级低于算术运算符,关系运算符的优先级高于赋值运算符 1.2关系表达式 用关系运算符将两个表达式(可以是算术表达式或关系表达式,逻辑表达式,赋值表达式,字符

Science|癌症中三级淋巴结构的免疫调节作用与治疗潜力|顶刊精析·24-09-08

小罗碎碎念 Science文献精析 今天精析的这一篇综述,于2022-01-07发表于Science,主要讨论了癌症中的三级淋巴结构(Tertiary Lymphoid Structures, TLS)及其在肿瘤免疫反应中的作用。 作者类型作者姓名单位名称(中文)通讯作者介绍第一作者Ton N. Schumacher荷兰癌症研究所通讯作者之一通讯作者Daniela S. Thomm

ffmpeg面向对象-待定

1.常用对象 rtsp拉流第一步都是avformat_open_input,其入参可以看下怎么用: AVFormatContext *fmt_ctx = NULL;result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL); 其中fmt_ctx 如何分配内存的?如下 int avformat_open_input(

09 生命周期

生命周期 beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestorydestoryed 辣子鸡:香辣入口,犹如吃了炫迈一样 - - - 根本停不下来 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"

chapter06 面向对象基础 知识点Note

文章目录 前言类的设计 属性和行为对象的内存解析 (堆 栈 方法区)类的成员之一 变量(属性) field类的成员之二 方法 method对象数组方法重载 overload可变个数的形参 语法糖方法的值传递机制递归关键字package importMVC设计模式import导入面向对象特征之一 封装类的成员之三 构造器JavaBeanUML类图 前言 ` 面向对象封装 面向

七、Maven继承和聚合关系、及Maven的仓库及查找顺序

1.继承   2.聚合   3.Maven的仓库及查找顺序

智能工厂程序设计 之1 智能工厂都本俱的方面(Facet,Aspect和Respect)即智能依赖的基底Substrate 之1

Q1、昨天分别给出了三个智能工厂的 “面face”(里面inter-face,外面outer-face和表面surface) 以及每个“面face” 各自使用的“方”(StringProcessor,CaseFilter和ModeAdapter)  。今天我们将继续说说三个智能工厂的“方面” 。在展开之前先看一下三个单词:面向facing,取向oriented,朝向toword。理解这三个词 和