Effective Objective-C 2.0学习记录(五)

news2024/11/18 15:23:05

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

使用委托模式:获取网络数据的类含有一个“委托对象”,在获取完数据后,它会回调这个委托对象。

请添加图片描述
利用协议机制,很容易就 能以OC代码实现此模式,在图中演示的情况下。可以这样定义协议:

@protocol EOCNetworkFetcherDelegate

- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveDate:(NSDate *)data;
- (void)networkFetcher:(EOCNetworkFetcher *)fether didFaillWithError:(NSError *)error;

@end

委托协议名通常是在相关类名后加上Delegate一词,整个类名用驼峰法来写。
有了这个协议后,类就可以用一个属性来存放其委托对象了。在本例中,这个类就是EOCNetworkFetcher类,此类的接口可以这么写:

@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak) id<EOCNetworkFetcherDelegate> delegate;
@end

这个id类型的协议属性一定要定义成weak,而非strong,因为两者之间必须为“非拥有关系”。一般情况下,扮演delegate的那个对象也要持有本对象,直到用完本对象之后,才会释放。
假如声明属性的时候用strong将本对象与委托对象之间定为“拥有关系”,那么就会引入“保留环”。因此, 本类中存放委托对象的这个属性要么定义为weak,要么定义为unsafe_unretained。如果需要在相关对象销毁时自动清空,则定义为前者,若不需要自动清空,则定义为后者。

请添加图片描述
实现委托对象的办法是声明某个类遵从委托协议,然后把协议中想实现的那些方法在类里实现出来。某类要遵从委托协议,可以在其接口中声明,也可以在“class-continuation分类”中声明。如果这个协议是委托协议,通常只会在类的内部使用,所以一般都是在“class- continuation分类”中声明。

委托协议中的方法一般都是可选的(optional)。所以需要用@optional关键字来标注其中的方法。

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

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

通过OC的分类机制,把类代码按逻辑划入几个分区中,这对开发和调试都有好处。


#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface EOCOPerson : NSObject
@property (nonatomic, copy, readonly) NSString* firstName;
@property (nonatomic, copy, readonly) NSString* lastName;
@property (nonatomic, strong, readonly) NSSet* friends;

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString *)lastName;

- (void)addFriend:(EOCOPerson *)person;
- (void)removeFriend:(EOCOPerson *)person;
- (BOOL)isFriendsWith:(EOCOPerson *)person;

-(void)performnDaysWork;
- (void)takeVacationFromWork;

- (void)goToTheCinema;
- (void)goToSportGames;
@end

NS_ASSUME_NONNULL_END

分划后:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface EOCOPerson : NSObject
@property (nonatomic, copy, readonly) NSString* firstName;
@property (nonatomic, copy, readonly) NSString* lastName;
@property (nonatomic, strong, readonly) NSSet* friends;

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString *)lastName;
@end

@interface EOCOPerson(Friendsship)
- (void)addFriend:(EOCOPerson *)person;
- (void)removeFriend:(EOCOPerson *)person;
- (BOOL)isFriendsWith:(EOCOPerson *)person;
@end


@interface EOCOPerson(Work)
-(void)performnDaysWork;
- (void)takeVacationFromWork;
@end

@interface EOCOPerson(Play)
- (void)goToTheCinema;
- (void)goToSportGames;
@end
NS_ASSUME_NONNULL_END

分划并分多个文件存储:

#import <Foundation/Foundation.h>


@interface EOCOPerson : NSObject
@property (nonatomic, copy, readonly) NSString* firstName;
@property (nonatomic, copy, readonly) NSString* lastName;
@property (nonatomic, strong, readonly) NSSet* friends;

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString *)lastName;
@end



//
//  EOCOPerson+Friendship.m
//
#import "EOCOPerson+Friendship.h"

@implementation EOCOPerson_Friendship

- (void) addFriend : (EOCOPerson *) person {
    /.../
}
- (void) removeFriend : (EOCOPerson *) person {
    /.../
}
- (BOOL) isFriendsWith : (EOCOPerson *) person {
    return YES;
}
@end



//
//EOCPerson+Work.m
//
#import "EOCPerson+Work.h"

@implementation EOCPerson_Work
- (void) performnDaysWork {
	/.../
}
- (void) takeVacationFromWork {
	/.../
}
@end


//
//EOCPerson+Play.m
//
#import "EOCPerson+Play.h"

@implementation EOCPerson_Play
- (void) goToTheCinema {
	/.../
}
- (void) goToSportGames {
	/.../
}
@end
  • 使用分类机制把类的实现代码划分成易于管理的小块。
  • 将应该视为“私有”的方法归入名叫Private的分类中,以隐藏实现细节。

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

分类机制通常用 于向无源码的既有类中新增功能。这个特性极为强大,但在使用时也很 容易忽视其中可能产生的问题。这个问题在于:分类中的方法是直接添加在类里面的,它们 就好比这个类中的固有方法。将分类方法加人类中这一操作是在运行期系统加载分类时完成 的。运行期系统会把分类中所实现的每个方法都加人类的方法列表中。如果类中本来就有此 方法,而分类又实现了一次,那么分类中的方法会覆盖原来那一份实现代码。实际上可能会 发生很多次覆盖,比如某个分类中的方法覆盖了“ 主实现” 中的相关方法,而另外一个分类 中的方法又覆盖了这个分类中的方法。多次覆盖的结果以最后 一个分类为准。

比方说,要给NSString 添加分类,并在其中提供一些辅助方法,用于处理与HTTP URL 有关的字符串。你可能会把分类写成这样:

@interface NSString (HTTP)

//Encode a string with URL encoding
- (NSString *)urlEncodeString;

//Decode a URL encoded string
- (NSString *)urlDecodedString;

@end 

如果还有 一个分类也往NSString 里添加方法,那会如 何呢?那个分类里可能也有个名叫urIEncodedstri ng 的方法,其代码与你所添加的大同小 异,但却不能正确实现你所需的功能。那个分类的加载时机如果晚于你所写的这个分类,那么其代码就会把你的那 一份覆盖掉。

所以应当:

以命名空间来区别各个分类的名称与其中所定义的方法。 想在Objective-C中实现命名空问功能,只有 一个办法,就是给相关名称都加上某个共用的 前缀。与给类名加前级(参见第15条)时所应考虑的因素相似,给分类所加的前缀也要选 得怡当才行。一般来说,这个前级应该与应用程序或程序库中其他地方所用的前缀相同。 于 是,我们可以给刚才那个NSString分类加上ABC前缀:

@interface NSString (ABC_HTTP)

//Encode a string with URL encoding
- (NSString *)abc_urlEncodedString;

//Decode a URL encoded string
- (NSString *)abc_urlDecodedString;

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

26.勿在分类中声明属性

属性是封装数据的方式。我们要尽量避免从坟里里声明属性。因为除了class-continuation分类之外,其他分类都无法向类中新增实例变量,因此他们无法把实现属性所需的实例变量合成出来。

如果我们把属性放在分类里时,会警告:此分类无法合成xxx属性相关实例变量。开发者需要在其分类中为属性实现存取方法。此时可以把存取方法声明为@dynamic。也就是说等到运行期在提供这些方法,编译器目前是看不见的

  • 关联对象能过解决在分类中不能合成实例变量的问题。这样做可行,但是不太理想,要把相似的代码写很多遍。而且在内存管理上容易出问题。因为我们在为属性实现存取方法时,经常会忘记遵从其内存管理语义。
  • 把封装数据所用的全部属性都定义在主接口里。
  • 在class- continuation分类之外的其他分类中,可以定义存取方法,但是进来不要定义属性。

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

“class-continuation分类”和普通的分类不同,他必须定义在其所连续的那个类的实现文件里,其重要之处在于,这是唯一能声明实例变量的分类,而且此分类没有特定的实现文件,其中的方法都应该定义在类的主实现文件里。与其他分类不同,“class- continuation分类”没有名字,也就是匿名类别及扩展(extension)。写法:

@interface EOCOPerson()
/.../
@end

这样你就可以在其中定义你的私有方法和私有变量了,这样有什么好处呢?公共接口里本来就能定义实例变量。不过,把它们定义在“class-continuation分类”或“实现块”中可以将其隐藏起来,只供本类使用。这些实例变量也并非真的私有,因为在运行期总可以调用某些方法绕过此限制,不过,从一般意义上来说,他们还是私有的。此外,由于没有声明在公共头文件里,所以将代码作为程序库的一部分来发行时,其隐藏程度更好。

  • 通过“class-continuation分类”向类中新增实例变量。
  • 如果某属性在主接口中声明为“只读”,而类的内部又要用设置方法修改此属性,那么就在“class-continuation分类”中将其扩展为“可读写”。
  • 把私有方法的原型声明在“class-continuation分类”里面。
  • 若想使类所遵循的协议不为人所知,则可于“class-continuation分类”中声明。

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

若是接口背后有多个不同的实现类,而你又不想指明具体使用哪个类,那么可以考虑用这个方法——因为有时候这些类可能会变,有时候他们又无法容纳于标准的类继承体系中,因而不能以某个公共基类来统一表示。此概念通常称为“匿名对象”。

@property (nonatomic, weak) id<EOCDelegete> delegate;

这个delegate就是“匿名的”,因为当你调用这个delegate的时候你并不知道它指的是那个类,而你却又能使用它所指代类的方法,这就把那个类给隐藏起来了,匿名对象也是同样的原理。

因为你可能定义很多的类,但是我们不能将它们都继承于同一个类,并且在OC中只有id类型可以将这些类的随便一个类都返回,所以我们在使用匿名对象的时候一定是返回的id类型。比如:我们将所有数据库都具备的那些方法放到协议中,令返回的对象遵从此协议。
先定义一个协议其中包括数据库都有的方法:

@protocol EOCDatabaseConnection
- (void)connect;
- (void)disconnect;
- (BOOL)isConneceted;
- (NSArray *)performQuery:(NSString *)query;
@end

提供一个单例接口:

#import <Foundation/Foundation.h>

@protocol EOCDatabaseConnection;

@interface EOCDatabaseConnection;
+ (id)sharedInstance;
- (id<EOCDatabaseConnection>)connectionWithIdentifier: (NSString *)identifier;

@end

这样的话,处理数据库连接的类名称就不会暴露了,来自不同框架的那些类限制就都可以使用同一个方法来返回了,而不用对每个类都写一个这种协议。

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

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

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

相关文章

【Java AWT 图形界面编程】Container 容器总结

文章目录一、AWT 简介二、AWT 核心类继承体系三、Container 容器类子类四、Container 容器常用 API五、Frame 窗口示例六、Panel 示例七、窗口中文乱码处理八、ScrollPane 可滚动容器示例一、AWT 简介 Java 中 使用 AWT 和 Swing 进行 图形界面开发 , AWT 是 抽象窗口工具集 , …

线程安全问题(3)

线程不安全:在多线程的调度情况下&#xff0c;导致出现了一些随机性&#xff0c;随机性是代码中出现了一些BUG&#xff0c;导致我们的线程是不安全的 造成线程不安全的原因: 1)操作系统抢占式执行&#xff0c;线程调度随机&#xff0c;这是万恶之源&#xff0c;我们无能为力 2)…

Web进阶:Day7 响应式、BootStrap、实战演练

Web进阶&#xff1a;Day7 Date: January 10, 2023 Summary: 响应式、BootStrap、实战演练 响应式 媒体查询 目标&#xff1a;能够根据设备宽度的变化&#xff0c;设置差异化样式 媒体特性常用写法 媒体特性常用写法&#xff1a; max-width&#xff08;从小到大&#xff0…

transformers包介绍——nlp界最顶级的包——可以不用 但不能不知道——python包推荐系列

背景1 现在在AI行业&#xff0c;什么最火&#xff1f;计算机视觉还是自然语言处理&#xff1f;其实不得不说&#xff0c;现在nlp很火。还有人记得上个月很多科技爱好者都在玩的chatgpt么&#xff1f;那个就是nlp技术的一大应用。现在都在觉得AI赚钱&#xff0c;工资高&#xf…

深度学习 12 正则化

1. 对于高方差(过拟合)&#xff0c;有以下几种方式&#xff1a; 获取更多的数据&#xff0c;使得训练能够包含所有可能出现的情况 正则化&#xff08;Regularization&#xff09; 寻找更合适的网络结构 2. 对于高偏差(欠拟合)&#xff0c;有以下几种方式&#xff1a; 扩大网…

【C语言进阶】只看此篇,让你学会动态内存管理

目录 前言 一、为什么存在动态内存分配 二、动态内存函数的介绍 1 、malloc和free 2、 calloc 3 、realloc 三、常见的动态内存错误 四、动态内存管理笔试题 1 题目1&#xff1a; 2 题目2&#xff1a; 3 题目3&#xff1a; 4 题目4&#xff1a; 五、C/C程序的…

5.10回溯法--圆排列问题--排列树

圆排列问题描述 给定n个大小不相等的圆&#xff0c;要将这n个大小不相等的圆排进一个矩形框中&#xff0c;且要求个个圆都与矩形框的最底边相切。要找出最小长度的圆排列。 问题分析 排列排列&#xff0c;解空间是一个排列树。 设开始时&#xff0c;a[n]储存n个圆的半径&…

笔试强训48天——day26

文章目录一. 单选1.在单处理器系统中&#xff0c;如果同时存在有12个进程&#xff0c;则处于就绪队列中的进程数量最多为&#xff08;&#xff09;2.在系统内存中设置磁盘缓冲区的主要目的是&#xff08;&#xff09;3.下列选项中&#xff0c;会导致进程从执行态变为就绪态的事…

时间复杂度空间复杂度

算法效率数据结构算法时间复杂度大O的渐进表示法三种时间复杂度几道例题一.简单递归二结合代码来判断时间复杂度空间复杂度我们程序猿开始的时候肯定听了不少的:数据结构和算法,那么阿涛就给大家说说自己的拙见吧.数据结构 数据结构就是我们用来组织数据的方式,比如我们可以把…

JVM-【面试题】-垃圾收集算法+垃圾收集器,以后就不用担心对象那些事了

一、垃圾收集算法在jvm里对可回收的对象在不同的垃圾收集器里&#xff0c;有不同的回收算法&#xff0c;具体的可以分为这四种&#xff1a;分代收集算法、复制算法、标记清除算法、标记整理算法1.1 分代收集算法当前虚拟机的垃圾收集都采用分代收集算法&#xff0c;这种算法没有…

【NI Multisim 14.0操作实例——最小锁存器电路】

目录 序言 &#x1f525;1.设置工作环境 &#x1f525;2.设置原理图图纸 &#x1f525; 3.设置图纸的标题栏 &#x1f525; 4. 增加元器件 &#x1f525; 5. 放置总线 &#x1f525; 6. 添加总线分支 序言 NI Multisim最突出的特点之一就是用户界面友好。它可以使电路…

ELasticSearch监控之Cerebro安装

文章目录安装包下载安装使用点击nodes 查看各节点状态点击rest发送可以发送请求查询数据安装包下载 https://github.com/lmenezes/cerebro/releases/download/v0.9.4/cerebro-0.9.4.tgzgit地址&#xff1a;https://github.com/lmenezes/cerebro 安装 将要包移动到/opt目录解…

Unity 3D 导入三维模型||Unity 3D 动画系统简介(Mecanim)

将三维模型导入 Unity 3D 是游戏开发的第一步。 下面以 3ds Max 为例&#xff0c;演示从三维建模软件中将模型导入 Unity 3D 的过程&#xff0c;具体步骤如下。 在 3ds Max 中创建房子模型。执行 Export → Export 命令导出 fbx 模型。设置保存路径以及文件名。选择默认设置选…

【transformers】tokenizer用法(encode、encode_plus、batch_encode_plus等等)

tranformers中的模型在使用之前需要进行分词和编码&#xff0c;每个模型都会自带分词器&#xff08;tokenizer&#xff09;&#xff0c;熟悉分词器的使用将会提高模型构建的效率。 string tokens ids 三者转换 string → tokens tokenize(text: str, **kwargs)tokens → strin…

安卓车机系统adb shell cmd 源码原理分析

hi&#xff0c;粉丝朋友们大家好&#xff01; 上一次视频分享了input专题课中input命令在android 12的更新&#xff0c;因为原来课程是基于android 10 &#xff08;可以加我扣&#xff1a;2102309716 优惠购买&#xff09; https://ke.qq.com/course/package/77595?tuin7d4eb3…

联合证券|金融部门开年推出新方案 改善优质房企资产负债状况

当时&#xff0c;努力促进房地产与金融正常循环是金融部门的工作要点之一。记者日前了解到&#xff0c;为遵循落实中心经济工作会议布置&#xff0c;有用防范化解优质头部房企危险&#xff0c;改进财物负债情况&#xff0c;有关部门起草了《改进优质房企财物负债表计划举动计划…

【计算机体系结构基础】流水线异常处理和提高流水线效率的技术(一)

流水线中的异常 异常的来源 外部事件指令执行中的错误数据完整性的问题地址转换异常系统调用陷入需要软件修正的运算 异常可以分为&#xff1a;可恢复异常和不可恢复异常 不可恢复异常&#xff1a;系统硬件出现严重故障&#xff0c;异常处理后系统面临重启。 解决办法&#x…

Spring Boot Apollo监听namespace并更新配置Bean(附源码)

这里是weihubeats,觉得文章不错可以关注公众号小奏技术&#xff0c;文章首发。拒绝营销号&#xff0c;拒绝标题党 背景 如果我们使用的配置中心是apollo的话我们经常会遇到这样的问题&#xff0c;就是动态更新配置Bean 动态更新配置bean 动态更新配置bean其实是很简单的&…

Python学习笔记-PyQt6消息窗

对话框是界面编程中重要的窗体&#xff0c;一般用于提示或者一些其他特定操作。一、使用QDialog显示通用消息框直接使用QDialog类&#xff0c;可以及通过对话框进行通用对话框显示&#xff0c;亦可以通过自定义设置自己需要的对话框。# _*_ coding:utf-8 _*_import sysfrom PyQ…

MySQL主从复制的原理是什么?

主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行(也叫重做)&#xff0c;从而使得从库和主库的数据保持同步。 MySQL支持一台主库同时向多台从库进行复制&#xff0c; 从库同时也可以作为其他从服务器的主…