OC协议和分类

前言

本篇文章介绍OC的分类和协议

分类

分类是这样一种设计,对于一个已经存在的类A,这个类可能是自己写的,或者是第三方的,甚至是系统提供的。这个类对我们很有用,但是,我们有些想要的功能在这个类里却没有实现。这个时候我们可以使用分类的功能对类A进行扩展。使用语法如下:

// 这个是A.h文件,定义了A的功能
@interface A : NSObject
{// 定义了一个实例变量,可以被子类使用int iA1;
}
@property int pA1;
-(void) A1;
@end
// 这是是分类A+B.h文件
#import "A.h"
// 分类的写法是这样的,主类名(分类名)
// 必须import主类名
@interface A(B)
{int iA_B1;
}
@property int pA_B1;
-(void)A_B1;
@end

分类完成后,如果我们想使用分类,必须import 分类的头文件

#import <Foundation/Foundation.h>
#import "A+B.h"// 这里需要包含分类名的头文件
int main(int argc, const char * argv[]) {@autoreleasepool {A* a = [[A alloc] init];[a A_B1];// 可以看到,A的实例可以调用分类的方法}return 0;
}

注意,我们在定义分类B的时候不需要列出类A继承的类。

分类有如下特性:

  • 如果我们在分类中定义属性,分类不会自动生成访问器方法。编译器会给出警告,并且如果我们调用访问器方法,运行会出现异常
  • 如果我们需要定义只读的属性,需要在属性定义时使用readonly限定符,这样就不会警告了。
  • 分类可以使用主类在接口中定义的实例变量,但是不可以使用主类在实现中定义的实例变量,因为那是私有的。这很好理解,我们如果给系统类实现一个分类,我们总不能访问系统类的私有实例变量吧。
  • 分类不能在接口中定义实例变量,但是可以在实现中定义,仅仅当前分类使用

分类的命名

如果我们需要将分类单独保存到一个文件,通常采用A+B的形式,其中A是主类名,B是分类名

类的扩展

有一种特殊的情况是创建一个未命名的分类,实现方法就是在定义分类时在括号中不指定名字。这种特殊的语法称为类的扩展,类的扩展一般是定义在主类的实现文件中,下面是类扩展的一个例子

// A.h文件
@interface A : NSObject
{int iA1;
}
@property int pA1;
-(void)A1;
@end// A.m文件
#import "A.h"
// 这是类A的扩展
@interface A()
{int iA_1;
}
@property int pA_1;
-(void)A_1;
@end@implementation A
-(void)A1
{NSLog(@"print A1");
}-(void)A_1
{NSLog(@"print A_1");
}
@end

如果我们在类的实现中定义类的扩展,扩展类有下面的属性:

  • 类的扩展中可以定义属性和实例变量,但是类的扩展中定义的属性,实例变量和方法都是私有的,只能在当前类内部访问,包括子类。通过这种方式,我们可以定义私有的东西。
  • 类的扩展中定义的方法必须在主类中实现,也就是说类扩展没有自己的实现部分
  • 不要在分类或者扩展中覆盖主类中的方法,如果需要覆盖,建议使用继承
  • 使用类扩展不仅会影响该类的所有用户,还会影响该类所有子类的用户。

协议和代理

协议的定义

协议是一组方法的接口
协议采用下面的格式进行定义

@protocol 协议名称
// 这里定义协议的接口
@required
@optional
@end

协议的使用

如果要在一个类中使用协议:

  • 首先要继承定义协议的类或者包含协议定义的头文件。
  • 并且把要遵守的协议名称放在类名和类继承的基类名称的后面,用<>括起来。
  • 如果采用多个协议,只需要将协议名都放进尖括号,用逗号分隔即可

看下面的例子:
首先是定义协议,下面的协议定义在A.h中

@protocol CALDelegate
@required
-(int)add:(int) a with:(int) b;
-(int)sub:(int) a with:(int) b;
-(int)times:(int) a with:(int) b;
-(int)div:(int) a with:(int) b;
@optional
-(int)add1:(int) a with:(int) b;
-(int)sub1:(int) a with:(int) b;
-(int)times1:(int) a with:(int) b;
-(int)div1:(int) a with:(int) b;
@end

注意:

  • @required:对于遵守协议名的类来说必须实现的方法列表
  • @optional:对于遵守协议名的类来说可以选择实现的方法列表

下面是遵守协议的类,有两种方式,一种通过继承:

@interface A1 : A<CALDelegate>

另一种通过包含A.h头文件

#import "A.h"
@interface C : NSObject<CALDelegate>

注意:协议是无类的,谁都可以遵守协议。

是否遵循协议

可以使用conformsToProtocol:方法检查一个对象是否遵循某项协议,看下面例子:

A1* a = [[A1 alloc] init];
if([a conformsToProtocol:@protocol(CALDelegate)])
{
NSLog(@"A1 conform Protocol CALDelegate");
}

注意:

  • 该方法只检查类是否遵循协议,并不代表该类实现了该协议下的方法
  • 事实上,就算你不实现任何协议定义的方法,只要遵循协议,也可以对该协议下的方法进行调用。但是运行的时候会报错,就算是@required方法,编译器也仅仅给出警告而已。

所以,一般情况下,我们如果调用协议下的方法,需要提前判断当前类是否已经实现了该方法。

代理

代理是协议的一种高级用法,通常的使用方式如下:

  • 在定义协议的类中定义一个代理类属性,该属性决定了代理类的性质和遵循的协议,看下面的例子:
    // A.h
    @protocol CALDelegate@required
    -(int)add:(int) a with:(int) b;
    -(int)sub:(int) a with:(int) b;
    -(int)times:(int) a with:(int) b;
    -(int)div:(int) a with:(int) b;@optional
    -(int)add1:(int) a with:(int) b;
    -(int)sub1:(int) a with:(int) b;
    -(int)times1:(int) a with:(int) b;
    -(int)div1:(int) a with:(int) b;@end
    @interface A : NSObject
    {int iA1;
    }
    @property int pA1;
    @property   NSObject<CALDelegate>* delegate;
    -(void)A1;
    -(void)print:(int)a with:(int)b with:(int)type;
    @end
    
    其中
    @property   NSObject<CALDelegate>* delegate;
    
    这行代码就是定义代理类的属性,说明代理类需要是NSObject或者子类,并且遵循CALDelegate协议
  • 在代理类中注册
    // C.m
    @implementation C
    -(void)print
    {A* a= [[A alloc] init];if(!a.delegate){// 代理类注册a.delegate = self;}// 在协议类中就可以使用代理类实现的协议方法了[a print:1 with:1 with:1];[a print:1 with:1 with:2];[a print:1 with:1 with:3];[a print:1 with:1 with:4];
    }
    // 后面是对协议方法的实现
    // ...
    

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

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

相关文章

Qt 三维柱状图 Q3DBar 和 三维条形图中的数据序列 QBar3DSeries

(一) 使用 Q3DBars 图形类和 QBar3DSeries 序列类可以绘制三维柱状图 窗口右侧是用 Q3DBars 和 QBar3DSeries 绘制的三维柱状图&#xff0c;这个图只有一个QBar3DSeries序列&#xff0c;数据是按行存储的&#xff0c;可以有多行。水平方向是行坐标轴和列坐标轴&#xff0c;使用…

Windows安装和使用kafka

一、安装kafka 由于kafka依赖jdk和zookeeper&#xff0c;安装kafka之前需要先安装jdk和zookeeper&#xff0c;也可以使用kafka自带的zookeeper。安装jdk可以参考&#xff1a;Windows和Linux安装jdk&#xff0c;此处使用kafka自带的zookeeper&#xff0c;不单独安装。 下面在Wi…

VUE2/3:element ui table表格的显隐列(若依框架)

若依框架自带一个组件&#xff0c;封装了关于表格&#xff0c;展示和隐藏表格列的功能&#xff1b; 使用效果就是这样的&#xff0c;在表格上面&#xff0c;三个框&#xff0c;从左到右分别是隐藏上面搜索&#xff0c;刷新列表&#xff0c;和显隐列的功能&#xff1b; 一、下面…

每日一题 82. 删除排序链表中的重复元素 II(中等,链表)

和昨天差不多&#xff0c;今天的是把所有重复数字的节点都删除&#xff08;昨天留了一个&#xff09; 显然当我们发现重复数字时&#xff0c;需要重复的第一个数字的前一个节点才能把重复数字删完&#xff0c;所有在while循环中我们每次判断 t.next 和 t.next.next 的值是否重复…

MySQL运维实战(4.1) MySQL表存储引擎

作者&#xff1a;俊达 MySQL表的特点 MySQL是一种开源的关系型数据库管理系统&#xff0c;与Oracle、SQL Server等数据库相比&#xff0c;有两个关键的特点&#xff1a; 存储引擎灵活性&#xff1a; MySQL的插件式存储引擎使得用户可以根据具体需求选择不同的引擎&#xff0…

互联网今年都崩盘了gis开发还有前途嘛?

互联网和GIS其实分不开的&#xff0c;尤其是在新兴技术领域。 互联网行业都已经在进军测绘、GIS以及智慧城市、无人驾驶等行业&#xff0c;随着高新技术的发展&#xff0c;互联网和GIS其实结合会越来越紧密。 传统互联网行业已经不能满足大众需求&#xff0c;近十年&#xff…

安装LibreOffice 解决soffice command was not found. Please install libreoffice

速览 1.安装原因1.准备文件1.1下载文件1.2解压文件 2.安装文件3.尝试运行3.1环境变量配置3.2安装附加依赖libxinerama13.3安装附加依赖libcairo23.4安装附加依赖libxt6 4.命令安装好像不行 1.安装原因 在使用LangChain框架时产生错误&#xff1a; soffice command was not fo…

TypeScript基础知识:模块化和命名空间

在现代的软件开发中&#xff0c;模块化和命名空间是构建可维护、可扩展和可重用代码的关键概念。TypeScript 提供了强大的支持来帮助我们组织和管理代码&#xff0c;本文将深入探讨 TypeScript 中的模块化和命名空间&#xff0c;并提供示例代码来帮助读者更好地理解这些概念。 …

如何应对Android面试官->我用RecyclerView实现了吸顶效果

前言 RecyclerView 计划用两个章节来讲解&#xff0c;今天主要是以 itemDecoration 和 实现吸顶效果为主&#xff1b; ItemDecoration ItemDecoration 允许应用给具体的 View 添加具体的图画或者 Layout 的偏移&#xff0c;对于绘制 View 之间的分割线&#xff0c;视觉分组边…

用bat脚本执行py文件以及批量执行py文件(全网超详细)

1.前言 对于python代码&#xff0c;每次执行一个文件就要运行一个命令&#xff0c;太过麻烦 在Windows电脑上&#xff0c;想一次性执行多个python文件的代码&#xff0c;就需要用到bat脚本 2.python代码 先写几个python代码的文件 如下图 3.py文件为中文&#xff0c;用bat执…

如何使用CFImagehost结合内网穿透搭建私人图床并无公网ip远程访问

[TOC] 推荐一个人工智能学习网站点击跳转 1.前言 图片服务器也称作图床&#xff0c;可以说是互联网存储中最重要的应用之一&#xff0c;不仅网站需要图床提供的外链调取图片&#xff0c;个人或企业也用图床存储各种图片&#xff0c;方便随时访问查看。不过由于图床很不挣钱&a…

CentOS中如何让新建用户拥有root权限

adduser newuser 新建用户newuser passwd newuser 设置密码 New UNIX password: Retype new UNIX password: 成功创建用户密码 passwd: all authentication tokens updated successfully. 2、赋予root权限 方法一&#xff1a; 修改 /etc/sudoers 文件&#xff0c;找…

WordPiece和SentencePiece区别

BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;模型的分词器通常使用子词级别的分词方法&#xff0c;其中最常用的分词器包括 WordPiece 和 SentencePiece。这些分词器用于将文本分成子词&#xff08;subwords&#xff09;或标记&#…

腾讯云价格计算器怎么用?太简单了一键报价

腾讯云服务器价格计算器可以一键计算出云服务器的精准报价&#xff0c;包括CVM实例规格价格、CPU内存费用、公网带宽收费、存储系统盘和数据盘详细费用&#xff0c;腾讯云百科txybk.com分享腾讯云价格计算器链接入口、使用方法说明&#xff1a; 腾讯云服务器价格计算器 打开腾…

❤ React报错问题分析

❤ React报错问题分析 ❤️ You passed a second argument to root.render(…) but it only accepts one argument. You passed a second argument to root.render(…) but it only accepts one argument. react-dom.development.js:86 Warning: You passed a second argumen…

css 居中方式

居中分为水平居中和垂直居中

医院患者满意度调查指标设计

医院患者满意度调查指标的设计是确保调查能够准确反映患者体验和医院服务质量的关键步骤。以下是一些常见的医院患者满意度调查指标&#xff0c;可以根据特定需求和目标进行定制&#xff1a; 整体满意度&#xff1a;通过一个综合评分或问卷问题来评估患者对整体医院体验的满意…

js对象和数组的区别

在JavaScript中&#xff0c;对象&#xff08;Object&#xff09;和数组&#xff08;Array&#xff09;是两种不同的数据结构&#xff0c;它们有着不同的用途和特性。 对象&#xff08;Object&#xff09;&#xff1a; 定义形式&#xff1a;对象是由键值对组成的&#xff0c;每个…

C++ 数组分页,经常有用到分页,索性做一个简单封装 已解决

在项目设计中&#xff0c; 有鼠标滑动需求&#xff0c;但是只能说能力有限&#xff0c;索性使用 php版本的数组分页&#xff0c;解决问题。 经常有用到分页&#xff0c;索性做一个简单封装、 测试用例 QTime curtime QTime::currentTime();nHour curtime.hour();nMin curtim…

各种排序算法学习笔记

Docshttps://r0dhfl3ujy9.feishu.cn/docx/XFlEdnqv9oCEoVx7ok8cpc4knnf?fromfrom_copylink如果你认为有错误&#xff0c;欢迎指出&#xff01;