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编程中的分类的使用,分类是一个非常重要的特性,有了分类就可以很容易实现模块化开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/296936.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10 年跟踪 Hacker News 招聘贴,解读科技行业变迁

Hackers News (HN) 是国外程序员最喜欢逛的论坛。能登上首页的帖子类似于上了新浪微博。因为其巨大的程序员访问量&#xff0c;因此也成为了公司招聘的渠道。久而久之 HN 招聘帖还形成了专门的标题格式 Ask HN: Who is hiring? 正好有人通过 Ask HN 来分析技术趋势&#xff0c…

【Blockchain】区块链浏览器 | 以太坊Etherscan比特币Blockchain门罗币Monero

区块链浏览器概述 区块链浏览器是一种软件,它使用API(应用程序编程接口)和区块链节点从区块链中提取各种数据&#xff0c;然后使用数据库来排列搜索到的数据&#xff0c;并以可搜索的格式将数据呈现给用户。 用户的输入是资源管理器上的可搜索项&#xff0c;然后通过数据库上…

《QT实用小工具·十》本地存储空间大小控件

1、概述 源码放在文章末尾 本地存储空间大小控件&#xff0c;反应电脑存储情况&#xff1a; 可自动加载本地存储设备的总容量/已用容量。进度条显示已用容量。支持所有操作系统。增加U盘或者SD卡到达信号。 下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #if…

数据库管理工具 DBeaverUE for Mac激活版

DBeaverUE for Mac是一款功能强大且易于使用的数据库管理工具&#xff0c;专为Mac用户设计。它支持多种数据库类型&#xff0c;如MySQL、PostgreSQL、Oracle等&#xff0c;使得用户可以轻松管理和操作各种数据库。 软件下载&#xff1a;DBeaverUE for Mac激活版下载 DBeaverUE …

【C语言】翻译环境与运行环境

一、前言 在我们学习C语言的时候&#xff0c;第一个接触的程序就是&#xff1a;在屏幕上打印” hello word! “&#xff0c;可当时的我们却未去深入的理解与感悟&#xff0c;一个程序代码是如何运行的&#xff1b;而这一期的博客&#xff0c;则是带着我们&#xff0c;通过C代码…

STM32的定时器中断Cubemx

STM32的定时器中断Cubemx 0.定时器简介1.配置时钟2.配置定时器3.创建工程4.补充源码 0.定时器简介 基本定时器功能&#xff1a; 16位向上、向下、向上/下自动装载计数器16位可编程(可以实时修改)预分频器&#xff0c;计数器时钟频率的分频系数为1&#xff5e;65535之间的任意…

向量旋转操作之分段递归交换

开篇 这是对于之前一维向量左旋操作问题的最后一个解法&#xff0c;也是关于这个问题的最后一篇文章。在之前的文章中&#xff0c;我们分别用求逆法、取模置换法对该问题进行了解答&#xff0c;今天&#xff0c;使用的是分段递归的方式。 问题概要 将一个n元一维向量向左旋转i个…

SpringBoot快速入门笔记(3)

文章目录 一、MybatisPlus1、ORM2、添加依赖3、全局配置4、Navicat5、UserController6、CRUD操作7、BaseMapper8、两个注解 二、多表查询1、模拟用户订单2、通过用户查相关订单3、UserMapperNew4、查询订单和所属用户5、OrderMapper6、OrderController 三、条件查询四、分页查询…

Java中生成一个唯一的文件名的方法

使用java.util.UUID&#xff08;通用唯一识别码&#xff09;的randomUUID()方法&#xff1a; import java.util.UUID;public class Test {public static void main(String[] args) {for (int i 0; i < 100; i) {String fileName UUID.randomUUID().toString();System.out…

element-ui card 组件源码分享

今日简单分享 card 组件源码&#xff0c;主要从以下两个方面&#xff1a; 一、card 组件页面结构 二、card 组件属性 2.1 header 属性&#xff0c;设置 header&#xff0c;也可以通过 slot#header 传入 DOM&#xff0c;类型 string&#xff0c;无默认值。 组件使用部分&#…

04 | Swoole 源码分析之 epoll 多路复用模块

首发原文链接&#xff1a;Swoole 源码分析之 epoll 多路复用模块 大家好&#xff0c;我是码农先森。 引言 在传统的IO模型中&#xff0c;每个IO操作都需要创建一个单独的线程或进程来处理&#xff0c;这样的操作会导致系统资源的大量消耗和管理开销。 而IO多路复用技术通过…

Redis的5大常见数据类型的用法

上一篇文章我们讲了Redis的10大应用场景&#xff0c;这一篇文章就针对Redis的常用数据结构进行一个说明&#xff0c;通过示例的形式演示每一种数据结构如何使用。 当涉及Redis的数据操作时&#xff0c;不同数据类型对应的不同数据结构&#xff0c;如下就对5大常用的数据类型进行…

稀疏矩阵的三元组表表示法及其转置

1. 什么是稀疏矩阵 稀疏矩阵是指矩阵中大多数元素为零的矩阵。 从直观上讲&#xff0c;当元素个数低于总元素的30%时&#xff0c;这样的矩阵被称为稀疏矩阵。 由于该种矩阵的特点&#xff0c;我们在存储这种矩阵时&#xff0c;如果直接采用二维数组&#xff0c;就会十分浪费…

数据结构—树

树概述 树类似于现实生活中倒置的树。任何一颗非空树只有一个根节点。一棵树具有以下特点&#xff1a; 一棵树中的任意两个结点有且仅有唯一的一条路径连通。一棵树如果有 n 个结点&#xff0c;那么它一定恰好有 n-1 条边。一棵树不包含回路。 下图就是一颗树&#xff0c;并…

ALPHA开发板上的PHY芯片驱动:LAN8720驱动

一. 简介 前面文章了解到&#xff0c;Linux内核是有提供 PHY通用驱动的。 本文来简单了解一下ALPHA开发板上的 PHY网络芯片LAN8720的驱动。是 LAN8720芯片的公司提供的 PHY驱动。 二. ALPHA开发板上的PHY芯片驱动&#xff1a;LAN8720驱动 我 们 来 看 一 下 LAN8720A 的 …

大模型量化技术-GPTQ

大模型量化技术-GPTQ 2022年,Frantar等人发表了论文 GPTQ:Accurate Post-Training Quantization for Generative Pre-trained Transformers。 这篇论文详细介绍了一种训练后量化算法,适用于所有通用的预训练 Transformer模型,同时只有微小的性能下降。 GPTQ算法需要通过…

【随笔】Git 基础篇 -- 分支与合并 git rebase(十)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

Linux提权!!!

上一篇文章讲了Windows的提权&#xff0c;那么这篇文章就来讲一下Linux的提权 1.SUID提权 suid权限 作用&#xff1a;让普通用户临时拥有该文件的属主的执行权限&#xff0c;suid权限只能应用在二进制可执行文件&#xff08;命令&#xff09;上&#xff0c;而且suid权限只能设置…

Vue依赖注入,详细解析

Prop 逐级透传问题​ 通常情况下&#xff0c;当我们需要从父组件向子组件传递数据时&#xff0c;会使用 props。想象一下这样的结构&#xff1a;有一些多层级嵌套的组件&#xff0c;形成了一颗巨大的组件树&#xff0c;而某个深层的子组件需要一个较远的祖先组件中的部分数据。…

优先队列c++

内容&#xff1a; priority_quene是一个优先队列&#xff0c;优先级别高的先入队&#xff0c;默认最大值优先 因此出队和入队的时间复杂度均为O&#xff08;logn&#xff09;,也可以自定义优先级 头文件<quene> 函数&#xff1a; 构建优先队列 priority_queue<in…