Effective_Objective-C_4协议与分类】

news2024/9/28 5:31:17

文章目录

  • 前言
  • 23.通过委托与数据源协议进行对象间的通信
        • 协议
            • 委托模式
            • 数据源模式
    • 要点总结
  • 24.将类的实现代码分散到便于管理的数个分类之中
        • 分类
        • Xcode创建一个分类
        • 分类需要注意什么
    • 要点
  • 25.总是为第三方的分类名称加前缀
    • 要点
  • 26.切勿在分类里面声明属性
        • 关联对象
        • 扩展可以添加属性
    • 要点
  • 27.使用“class-continuation分类”隐藏实现细节
        • class-continuation分类的实质作用
        • class-continuation用法
    • 要点
  • 28.通过协议提供匿名对象
    • 要点
  • 总结

前言

  • 第四章的名称叫协议和分类,这两个词都是之前比较熟悉的老朋友了。协议是OC语言的哟哥特性,它和Java语言的接口类似,OC是不支持多重继承的,所以某个类的具体实现可以放到协议里面。协议最常见的实现是委托模式。
  • 分类也是OC语言的重要特性之一,分类的好处就是我们不要继承子类就可以直接的为当前类添加方法,这也是OC语言动态性的体现之一。

23.通过委托与数据源协议进行对象间的通信

  • 在OC语言里,委托模式是对象之间进行相互通信的主要方法之一。该模式是定义一套接口,某个对象需要接受另一个对象的委托的时候,需要遵从这个接口,就成为了其委托对象
  • 具体实现可以把对象分为两种,数据源和委托

协议

委托模式
  • OC语言一般通过协议来实现委托模式,委托协议的命名采取驼峰命名法,一般在名词之后添加Delegate这个单词。
  • 协议实现的过程如下
    请添加图片描述
  • 定义协议的时候需要注意,属性的修饰不能用strong,需要weak或者unsafe_unretained,因为本对象和委托对象之间的关系是非拥有关系。如果采用了strong修饰则会出现本对象和委托对象之间的拥有关系,存在保留环。
    • 如果需要相关对象销毁的时候字典清空则用weak,若不要自带情况则unsafe_unretained
  • 实现委托对象的办法是声明某个类遵从委托协议,然后把协议中想实现的那些方法在类中实现出来。如果要向外界公布此类实现了某协议,那么就在接口处声明,而如果这个协议是个委托协议的话,那么通常只会在类的内部使用。也就是在.m文件中遵循协议
    请添加图片描述
  • 委托协议的方法一般都是可选的,因为扮演受委托者的角色对象未必关心所有方法,所以委托协议经常使用@optional关键字
    请添加图片描述
  • 如果需要在委托对象对象上调用可选方法,需要提前使用类型信息查询方法判断一下委托对象是否实现了相关方法,如果实现了就调用即可,没实现不需要执行任何操作,哪怕给nil对象发送消息程序也能照常运行
  • 需要注意的是在delegate对象实现相关方法的时候把发起委托的实例一并传入方法里面就能根据传入的实例不同来执行不同的代码,对于多重选择委托对象来说挺方便的。
数据源模式
  • 同样可以定一套接口令某个类经过该接口获得所要的数据,datasource模式。在这个模式里信息从数据源流向了类,而在委托模式里面信息则是从类流向委托者,这是二者的区别和联系
    请添加图片描述
  • 在写代码的时候对上述还有存在优化:将委托对象是否能响应相关协议方法这一信息缓存至C语言中的“位段”数据类型了,使用这种数据类型将方法的相应能力缓存起来,在进行判断的时候只需要将缓存调出判断即可。

要点总结

  • 委托模式为对象提供了一套接口,使其可由此将相关事件告知其他对象。
  • 将委托对象应该支持的接口定义成协议,在协议中把可能需要处理的事件定义成方法。
  • 当某对象需要从另外一个对象中获取数据时,可以使用委托模式。这种情况下,该模式亦称“数据源协议”。
  • 若有必要,可实现含有位段的结构体,将委托对象是否能响应相关协议方法这一信息缓存至其中。

24.将类的实现代码分散到便于管理的数个分类之中

  • 这一条就是介绍了如何使用分类这条性质。
  • 对于类里面经常容易被各种方法填满,随着方法的增多某个类的文件逐渐增多和庞大起来,久而久之影响效果,所以OC存在一种分类机制,把代码按照逻辑划分到几个分区里面,这对于开发和调试都是有着极大的好处

分类

  • 对于某个信息可能存在这些方法
    请添加图片描述
@interface iOSPerson : NSObject
@property (nonatomic, strong)NSString *firstName;
/*FriendSHip*/
- (void)addFriend:(iOSPerson *)person;
- (void)removeFriend:(iOSPerson *)preson;
- (BOOL)isFriend:(iOSPerson *)person;
/*Work*/
- (void)performDayWork;
- (void)takeVacationFromWorl;
/*Game*/
- (void)goToTheCinema;
- (void)goToSportFIFA22;
@end
  • 上述代码经过分类思想可以变成如下代码
    请添加图片描述
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface iOSPerson : NSObject
@property (nonatomic, strong)NSString *firstName;
@end
@interface iOSPerson (FriendShip)
/*FriendSHip*/
- (void)addFriend:(iOSPerson *)person;
- (void)removeFriend:(iOSPerson *)preson;
- (BOOL)isFriend:(iOSPerson *)person;
@end

@interface iOSPerson (Work)
/*Work*/
- (void)performDayWork;
- (void)takeVacationFromWorl;
@end

@interface iOSPerson (Game)
/*Game*/
- (void)goToTheCinema;
- (void)goToSportFIFA22;
@end
NS_ASSUME_NONNULL_END

    • 现在类的实现代码按照方法分成了好几个部分,使用分类医护依然可以把整个类定义在一个接口里面,不过随着分类的增加还是需要把分类提取到各自的文件里面最为合适。

Xcode创建一个分类

  • 1) 点开一个文件 点击New File ,选择iOS的OC File
    在这里插入图片描述

  • 创建类别:在FIle 哪一行写上类别名字,第二行选择 Categroy(类别) 第三行是要被添加的类,即可创建
    在这里插入图片描述

  • 完成创建
    在这里插入图片描述

    • 接着就是实现对应的代码了
  • 通过分类机制,可以把类代码分成多个易于管理的小块,以便单独检视。虽然它引入文件时会稍微有点麻烦,但是他还是一种管理代码的好方法,并且在调试程序的时候它还会明确的指出相应的分类,这就对我们调试程序有很大的帮助,所以说使用分类来规划代码的利大于弊。

  • 并且我们之前有说过私有方法的命名,通过特殊的前缀将私有方法指示出来,那么我们学了分类规划之后,我们还可以通过创建一个分类,这个分类其中全是私有方法,通过这种方法将这些私有方法都规划到一个类中,当然其还是的遵循之前的命名规则。

分类需要注意什么

  • 不能添加属性!!!!下面26条会说到

要点

  • 使用分类机制把类的实现代码划分成易于管理的小块。
  • 将应该视为“私有”的方法归入名叫Private的分类中,以隐藏实现细节

25.总是为第三方的分类名称加前缀

  • 分类机制通常用于向无源码的既有类中新增功能,但是他也存在相应的问题:分类中的方法是直接添加在类里面的,他们就好比这个类中的固有方法。将分类方法加入类中这一操作是在运行期系统加载分类时完成的。运行期系统会把分类中所实现的每个方法都加入类的方法列表中。如果类中本来就有此方法,而分类又实现了一次,那么分类中的方法就会覆盖原来的那一份实现代码。并且多次覆盖的结果以最后一个分类为准。
  • 所以我们要做的就是总是为第三方的分类名称加前缀
    请添加图片描述
  • 这么做能大大减小代码偶然性的概率,也便于调试

要点

  • 向第三方类中添加分类时,总应给其名称加上你专用的前缀。
  • 向第三方类中添加分类时,总应给其中的方法名加上你专用的前缀。

26.切勿在分类里面声明属性

  • 分类只能添加新的方法,但是不能添加属性(成员变量),我们尝试添加成员变量会出现警告
Property 'age1' requires method 'setAge1:' to be defined - use @dynamic or provide a method implementation in this category属性“age1”需要定义方法“setAge1:”—请使用@dynamic或在此类别中提供方法实现
  • 这个警告只是需要我们给 用@property关键字 添加的属性手动完成setter getter方法,但是当我们在写setter getter方法的时候一旦涉及到我们在类别定义的属性的时候就会报错

关联对象

  • 关联对象也可以解决在分类中不能合成实例变量的问题
    请添加图片描述
    • 这样做有一点麻烦的地方就是相似的代码需要写很多遍,容易在内存管理方面出错。书上提示正确的做法就是把属性都顶柜i在主接口里面,分类只是在于拓展功能的方便,并不是封装数据的方便之处,不过分类里可以存放一些可读的属性,这样不会访问数据本身,自然也不需要实例变量来实现它们

扩展可以添加属性

  • 之前还了解过扩展,和类别的区别就是 在第二行选择Extension 其他的不变
    • 可以为待扩展的类添加额外的 属性 变量 和方法生命
    • 注意私有属性写在类扩展
    • 扩展可以添加属性和成员变量
    • 扩展是本身没有自己的实现的,它和本类共享一个实现

要点

  • 类别和扩展都可以为 原来的类添加新的方法,但是类别的方法不实现系统不会提供警告,扩展的方法不提供实现 系统会提示警告
    • 原因是分类在运行的时候就被添加到类中了,扩展在编译的时候阶段被添加到类中
  • 把封装数据所用的全部属性都定义在主接口里。
  • 在“class-continuation分类”之外的其他分类中,可以定义存取方法,但尽量不要定义属性

27.使用“class-continuation分类”隐藏实现细节

  • class-continuation分类和普通的分类不同,它必须定义在其所连续的那个类的实现文件里,而且这是唯一能声明实例变量的类。
  • class-continuation分类还作用在我们实现不需要对外进行公开的部分。
  • 请添加图片描述
  • class-continuation分类写法如上,不带名字。

class-continuation分类的实质作用

  • class-continuation分类的存在一定是有意义的,意义就在于可以给类定义方法和变量而且不被外界访问,只提供给本类使用,并且和类的实现文件放在一起实现隐藏细节

请添加图片描述

class-continuation用法

  • 如果你既想要外界知道你有这个属性,但是又不能让外界访问,我们一般会把它改成可读的,如果使用class-continuation在实现文件里面改成读写,就是说,你在外部.h文件中定义一个“只读”的属性,然后你又在“class-continuation分类”将其的“只读”属性改为“可读写”的,那么这样下来,在外部看来他就是一个“只读”的属性,但是你可以在其内部自定义的设置其值了他在内部来说就是“可读写”的了
    • 这样做很有用,既能令外界无法修改对象,又能在其内部按照需要管理其数据。这样,封装在类中的数据就由实例本身来控制,而外部代码则无法修改其值。
  • 若对象遵从的协议只应该视为私有,那么直接在 class-continuation 分类里面声明即可。请添加图片描述

要点

  • 通过“class-continuation分类”向类中新增实例变量。
  • 如果某属性在主接口中声明为“只读”,而类的内部又要用设置方法修改此属性,要么就在“class-continuation分类”中将其扩展为“可读写”。
  • 把私有方法的原型声明在“class-continuation分类”里面。
  • 若想使类所遵循的协议不为人所知,则可于“class-continuation分类”中声明。
  • 这一点是我觉得用处很大的一点,“class-continuation分类能够在保证私密性的前提下还能够合理的使用数据,隐藏数据

28.通过协议提供匿名对象

  • 利用协议把自己写的API的细节隐藏起来,将返回的对象设置为遵从协议的纯id类型,这样就达到了匿名对象的效果,因为在OC里id类可以指代任何的一个类型,此概念就叫匿名对象。
  • 在用到协议传值的地方经常遇到这样的写法
    请添加图片描述
@property (nonatomic, weak)id<returnFoodNameDelegate>foodDetailsDelegate;
  • 实际上任何类的任何对象都可以充当foodDetailsDelegate这个属性,对于具备此属性的类来说,foodDetailsDelegate就实现了匿名效果。

要点

  • 协议可在某种程度上提供匿名类型,具体的对象类型可以淡化成遵从某协议的id类型,协议里规定了对象所应实现的方法。
  • 使用匿名对象来隐藏类型名称(或类名)。
  • 如果具体类型不重要,重要的是对象能够响应(定义在协议里的)特定方法,那么可使用匿名对象来表示。

总结

  • 协议和分类这一章也是讲述了使用协议和分类需要注意的细节和很多能够在实际用途用到的方法,比如使用class-continuation分类隐藏实现细节等等

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

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

相关文章

用javascript分类刷leetcode20.字符串(图文视频讲解)

1143. 最长公共子序列 (medium) 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删…

系分 - UML【概念】

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录UML - Unified Modeling LanguageUML中有4种事物结构事物行为事物分组事物注释事物UML图的分类结构型图&#xff08;静态图&#xff09;行为型图&#xff08;动态图&#xff09;UML图 - 静态图[结构型]类图…

C#枚举器和迭代器

C#枚举器和迭代器 使用foreach语句时&#xff0c;可以依次取出数组里面的元素&#xff0c;原因就是数组提供了“枚举器&#xff08;Enumerator&#xff09;”&#xff0c;枚举器知道元素的位置并返回请求项。 枚举器IEnumerator 枚举器实现了IEnumerator接口&#xff0c;该接…

Angular页面使用指令和路由守卫进行权限控制

在各种业务系统中&#xff0c;为了保证业务及数据安全&#xff0c;除了要求用户必须登录后才能操作外&#xff0c;还针对不同的角色对不同用户设置了各自的访问权限&#xff0c;包括确定的某个页面的权限和页面中特定元素的权限。本文记录了一种Angular页面常用的权限管理方法。…

C++:std::function模板类

一&#xff1a;function定义 类模板 std::function是一种通用的多态函数包装器&#xff0c;它的实例可以对任何可以调用的目标实体进行存储&#xff0c;复制和调用操作。简单的来说&#xff1a;C中有几种可调用对象&#xff1a;函数&#xff0c;指针&#xff0c;lambda表达式&…

区块链之开发命令行操作模块

文章目录功能介绍go语言中flag用法简介项目命令行具体实现链接&#xff1a; 区块链项目github地址项目目前进度&#xff1a;功能介绍 利用命令行操作区块链相较于图形用户界面来说&#xff0c;编写代码简单&#xff0c;同时也可以实现复杂的功能。命令行模块的功能应该满足&am…

Java学习笔记 --- JDBC(1)

一、JDBC概述 基本介绍 1、JDBC为访问不同的数据库提供了统一的接口&#xff0c;为使用者屏蔽了细节问题 2、Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统&#xff0c;从而完成对数据库的各种操作 3、JDBC原理图 JDBC带来的好处 JDBC是Java提供一套用于数…

安卓移动端调用自然语言处理nlp模型【示例+源码】

安卓可以使用许多不同的方法来调用NLP模型。其中一种方法是使用现有的自然语言处理库,例如 Apache OpenNLP、 Stanford NLP 和 NLTK。这些库提供了许多常用的 NLP 功能,如分词、词干化、命名实体识别和词性标注。另一种方法是使用 TensorFlow Lite 或其他机器学习框架来加载并…

[ins 2022] 针对已见和未见群体的群体推荐中的贝叶斯归纳学习

Bayesian inductive learning in group recommendations for seen and unseen groupshttps://www.sciencedirect.com/science/article/pii/S0020025522008933摘要群组推荐是指向一组用户&#xff08;即成员&#xff09;推荐物品。在预测相关项目时&#xff0c;模型通常会面临未…

fs 文件系统模块

1、什么是 fs 文件系统模块 fs 模块是 Node.js 官方提供的、用来操作文件的模块。它提供了一系列的方法和属性&#xff0c;用来满足用户对文件的操作需求。 方法名 说明 fs.readFile() 用来读取指定文件中的内容 fs.writeFile() 用来向指定的文件中写入内容 如果要在 J…

webflux整合swagger教程V2版

1. yml文件配置 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId><version>2.1.0.RELEASE</version></dependency><!--数据库开始--&…

python @classmethod

1..什么是classmethod classmethod是用来指定一个类的方法为类方法 长的像下面这个样子 1 2 3 class cc: classmethod def f(cls, arg1, arg2, ...): ... cls通常用作类方法的第一参数 跟self有点类似&#xff08; __init__里面的slef通常用作实例方法的第一参数)。…

vue开发环境配置Visual Studio Code配置和安装教程

方便前端vue开发&#xff0c;使用vs code插件安装详细教程&#xff0c;关于vs code可以网络上相关的教程&#xff0c;插件安装如下图所示&#xff0c;大家常用的插件可再分享&#xff0c;与我联系。 1 安装Vue语法高亮显示插件&#xff1a;vetur 2 安装vue语法提示插件&#xf…

小孩上了半年小学,针对老师的评语总结,如何对症优化教育培养策略?chatGPT搜了一下,AI震惊了我

评语 班主任评语&#xff1a; 你是一个性格内向、聪明伶俐的男孩。平时能按时完成老师布置的作业&#xff0c;学习认真&#xff0c;成绩优良&#xff0c;做事认真。但有时自己的事情还不能自己完成&#xff0c;希望你以后可以独立起来&#xff0c;遇到问题多想办法&…

高性能网关基石——OpenResty

什么是 OpenRestyOpenResty 一个基于 Nginx 的高性能 Web 平台&#xff0c;能够方便地搭建处理超高并发的动态 Web 应用、 Web 服务和动态网关。例如有名的 Kong 网关和国产新秀 ApiSIX 网关都是基于 OpenResty 来进行打造的。OpenResty 通过实现 ngx_lua 和 stream_lua 等 Ngi…

Nmap工具使用

Nmap工具使用1.Nmap简介1.1.Nmap介绍1.2.Nmap功能介绍1.3.Nmap下载1.4.Nmap端口状态2.Nmap基本使用2.1.Nmap基础扫描2.2.Nmap基础扫描多个目标2.3.Nmap详细扫描输出2.4.Nmap指定端口扫描2.4.1.单端口扫描2.4.2.端口范围扫描2.4.3.端口组合扫描2.5.Nmap扫描排除2.5.1.排除一个主…

强大的ANTLR4(6)--设计语法

四种抽象的计算机语言模式&#xff1a; 1&#xff09;序列&#xff1a;一列元素&#xff0c;数组 2&#xff09;选择&#xff1a;在多个可选方案中做出选择 3&#xff09;词法符号依赖&#xff1a;例如左右括号匹配 4&#xff09;嵌套结构&#xff1a;一种自相似的语言结构。 …

lambda表达式入门

一、函数式编程思想 1 概念 面向对象思想需要关注用什么对象完成什么时期&#xff0c;而函数式编程思想更类似于我们数学中的函数&#xff0c;它主要关注的是对数据进行了什么操作 2 优点 代码简洁&#xff0c;开发快速接近自然语言&#xff0c;易于理解易于"并发编程…

计算机网络——BGP协议

BGP协议 和谁交换&#xff1a;与其他AS的邻站BGP发言人交换信息。 交换什么&#xff1a;交换网络可达性信息 多久交换一次&#xff1a;发生变化时更新有变化的部分 一般来说两个网络都是由一个BGP发言人连接的。 BGP协议交换信息的过程 BGP协议所交换的网络可达性的信息就…

Haproxy 代理后端服务

参考 http://www.haproxy.org HAProxy GitHub 目录 一、Haproxy环境准备 1、Haproxy简介 1.1、haproxy原理 1.2、Haproxy优点 2、在线apt安装 二、使用Haproxy 1、基本脚本结构示例 2、配置反向代理 3、验证haproxy 3.1、重启服务 3.2、访问后台管理 3.3、访问…