12. 理解消息转发(message forwarding)

紧接着第11条的消息传递机制,如果对象无法解读接收到的消息时就会启动消息转发机制。

向类发送其无法解读的消息时,编译期不会报错,只有运行期才可以检查出来。

消息转发有两个阶段:

(一)动态方法解析

对象收到无法解读的消息后,首先调用类的方法:

+ (BOOL)resolveInstanceMethod:(SEL)selector;//selector是实例方法
+ (BOOL)resolveClassMethod:(SEL)selector;//selector是类方法
复制代码

使用此方法的前提是相关方法的实现代码已经写好,只等着运行的时候动态插在类里面

表示这个类是否能新增一个实例方法来处理这个选择器。

(二)完整的消息转发机制

(1)备援接收者

当前接收者还有第二次机会能处理未知的选择器,对应的方法如下:

- (id)forwardingTargetForSelector:(SEL)selector;
复制代码

(2)完整消息转发

首先创建NSInvocation对象,把与尚未处理的那条消息有关的全部细节都封于其中,包含选择器、目标以及参数。消息派发系统(message-dispatch system)将调用下面方法来转发消息:

- (void)forwardInvocation:(NSInvocation *)invocation;
复制代码

实现此方法较为有用的方式是:在出发消息前,先以某种方式改变消息内容,比如追加另外一个参数或者改变选择器等。如果某调用操作不由本类处理,需要调用超类的同名方法,直至NSObject。最后还是不能调用方法,就抛出异常“doesNotRecognizeSelector”。

综上全部的消息转发流程可见下图:

13. method swizzling(方法混合)

此方法堪称经典的“黑魔法”。

类似于C++的虚函数表,OC的类也有方法列表,列表项会将选择器的名称映射到方法的实现指针上。比如NSString的几个常用方法映射情况:

OC Runtime系统方法可以操作这个表,具体地,开发者可以向其中增加选择器或者交换选择器的实现方法。经过几次改变后的映射表变成如下:

可见method swizzling可以无需编写子类就可以实现如此强大的功能。

具体的交换代码逻辑如下:

Method originalMethod = class_getInstanceMethod([NSString Class],@selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString Class],@selector(uppercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);
复制代码

实际应用中,仅仅交换两个方法的实现是意义不大的,一般我们使用这种手段来为方法新增一些功能,比如埋点。我们需要知道一共调用多少次lowercaseString, 如果我们没个调用的地方都去埋点,就很麻烦也很容易漏掉统计,这时候我们就可以用方法混合技术简单实现。

我们先做一个NSString 的分类

@interface NSString (EOCMyAdditions)
- (NSString*)eoc_myLowercaseString;
- @end@implementation NSString (EOCMyAdditions)
- (NSString *)eoc_myLowercaseString {NSString *lowercase = [self eoc_myLowercaseString];NSLog(@"%@",lowercase);return lowercase;
}
@end
复制代码

注意,这里会让人误解为死循环调用,其实不是,在调用此方法前,已经做过"lowercaseString"与"eoc_lowercaseString"方法的交换了,此时"[self eoc_myLowercaseString]"实际上会调用的是“lowercaseString“。

至此,所有NSString实例调用lowercaseString方法都会输出日志。

14. 理解"类对象"

这个关于OC类对象继承体系与元类的知识点在很多地方都会讲到,详细知识点直接去参考runtime中关于Class类的相关定义。

本节的重点有几个:

1)类型信息查询(introspection,内省),在运行时起检查对象的类型。

isMemberOfClass可以判断出对象是否是某个特定类的实例; isKindOfClass可以判断出对象是否是某类或者其派生类的实例。还可以使用==操作符来判断对象是否是某类的实例([obj class] == [EOCSomaClass Class])

我们应该尽可能使用内省的方法而非直接比较类对象方法来判断,因为前者可以正确处理那些使用了消息传递机制的对象。比如代理对象(NSProxy),在此代理对象上调用class方法返回的是代理对象本身而非接受代理的对象类;然而使用isKindOfClass,代理对象就会吧这条消息转发给接受代理的对象。

2)类对象结构

OC中每个对象结构体的首个成员是Class类的变量,该变量定义了对象所属的类,称为"is a"指针。Class结构体存放类的“元数据”,其首个变量也是isa指针,说明Class本身也是Objective-C对象,这个isa表示类对象是一个“元类”类型的对象。类方法就放在元类中,类似于实例方法放在类对象中一样。

每个类仅有一个类对象,每个类对象仅有一个元类

15. 用前缀避免命名空间冲突

OC没有namespace机制,需要前缀命名法避免重复符号。

避免命名空间冲突的场景主要有:

1) 类名,一般加上公司、App有关联的前缀名。

2)纯C函数与全局变量,因为它们算作顶级符号,会与其他地方定义的函数或变量重名。

3)使用第三方库(a)再次开发自己的第三方库(b)时,别人在使用你发布的第三方库(b)的应用程序里可能也引 入了第三方库(a),此时应该将b中引入的a库代码上都加上你自己的前缀。

16. 提供全能初始化方法

可为对象提供必要信息以便其完成工作的初始化方法叫做“全能初始化方法”。

一个类可能有很多个初始化方法,必须有一个全能初始化方法,让其他所有初始化方法都调用它。

如果子类的全能初始化方法与超类的方法名称不同,那么应该覆写超类的全能初始化方法。如果超类的全能初始化方法不适用于子类,可以覆写超类的方法并在其中抛出异常。

17. 实现description方法

在打印对象信息时用到:

NSLog(@"%@",obj);
复制代码

可以自定义上述的输出格式。

NSObject还有一个debugDescription方法,此方法是为了自定义控制台po命令时的对象信息输出格式。 我们可能不想把类名与指针地址等信息放在普通的描述信息中,确又想在调试时候能看到他们,就可以实现两个描述方法。NSArray就这这么做的。

18. 尽量使用不可变对象

实际编码时,尽量把对外公布的属性设为只读,而且只在确有必要时才将属性对外公布。当然,我们可以在类的内部实现中再次将这些属性设置为读写的。

在表示各种collection的属性时,可以设为不可变的,然后提供修改方法操作这个collection,内部维持一个可变的collection,返回其拷贝给外部。

19. 使用清晰协调的命名方式

OC方法名一般相对比较冗长,但是却可以相对清晰完整地表达含义,向日常句子一样。

使用OC规范的驼峰命名法。

坚持统一风格。

20. 为私有方法名加前缀

这样容易将私有方法与公有方法区别开来,方便调试。

建议使用p_前缀。

不要用一个单一的下划线做前缀,这是苹果公司预留的。

21. 理解OC错误模型。

OC不像Java那样频繁地抛出异常,因为OC很难做到“异常安全”。在ARC模式下,抛出异常后,那些本应该在作用域末尾释放的对象就不能自动释放了。非ARC模式下,即使在抛出异常之前手动释放资源,但是如果释放的资源太多或者代码执行路径很复杂时,就会使代码和乱。

可以设置编译器标志来实现异常安全代码,即打开-fobjc-arc-exceptions。但这样会引入额外代码,在不抛出异常时,也会执行代码。

所以只有发生了导致crash的严重错误时,才使用异常。

其他不严重的情况,可以使用delegate来处理NSError对象。

22. 理解NSCopying协议

自定义对象要支持拷贝操作,需要实现NSCopying协议,并覆写

- (id)copyWithZone:(NSZone*)zone;
复制代码

方法,这里的NSZone是历史遗留问题,现在都是default zone,不用管。

同理,如果要获取可变版本的拷贝时,需要遵守NSMutableCopying协议并覆写

- (id)mutableCopyWithZone:(NSZone*)zone;
复制代码

方法。

拷贝时,有深拷贝与浅拷贝之分。

深拷贝: 在拷贝对象本身时,将其底层数据也一并复制过去。 浅拷贝: 只拷贝对象本身,不复制其中数据。

Foundation框架中的所有collection类默认情况下都执行浅拷贝

NSSet 有一个深拷贝的初始化方法:

- (id)initWithSet:(NSArray *)array copyItems:(BOOL)copyItmes;
复制代码

《Effective Objective-C 2.0》 阅读笔记 2相关推荐

  1. PREMIS元数据字典3.0阅读笔记(一)

    一.3.0版本与之前版本区别 将知识实体重新定位为对象类别,以便在 PREMIS 中进行附加描述并链接到相关 PREMIS 实体. 重新定位环境(即使用数字对象所需的硬件和软件),以便可以重复使用对象 ...

  2. Solr入门之官方文档6.0阅读笔记系列(八) 相关过滤器

    第三部分 :   Understanding Analyzers, Tokenizers, and Filters Filter Descriptions You configure each fil ...

  3. [iOS开发]——系统框架(effectiveOC2.0阅读笔记)

    系统框架 第47条:熟悉系统框架 要点 第48条:多用块枚举,少用for循环 for循环 使用 Objective-C 1.0的 NSEnumerator 来遍历 快速遍历 基于块的遍历方式 要点 第 ...

  4. JSF 2.0阅读笔记:视图状态 (四)

    五. 实例验证 下面使用GlassFish v3来测试JSF2.0 RI的ViewState情况. JSF2.0规范中提供了三个上下文参数来指定应用的ViewState策略,可在web.xml文件中使 ...

  5. 【《RISC-V “V“ Vector Extension Version 1.0》阅读笔记】

    <RISC-V "V" Vector Extension Version 1.0>阅读笔记 RISC-V "V" Vector Extension ...

  6. Xilinx AXI USB2.0 Device IP 手册阅读笔记

    目录 1. 前言 2. 概要 2.1 USB2.0协议特点: 2.2 The AXI USB 2.0 Device介绍 2.2.1 Endpoint0: 2.2.2 Endpoint1~7: 3. 详 ...

  7. 《通信统一架构OPC UA实践》 - 阅读笔记 1 工业4.0基础与OPC UA的实践

    系列目录 - 阅读笔记 2 OPC UA信息模型及建模 目录 系列目录 第一章 OPC UA--工业4.0基础 1.1.通信协议 1.2.导入OPA UA的步骤 1.3. 趋势和展望 第二章 OPC ...

  8. T5: Text-to-Text Transfer Transformer 阅读笔记

    作者:徐啸 知乎专栏:自然语言处理学习之旅 https://zhuanlan.zhihu.com/p/89719631 写在前面 谷歌用一篇诚意满满(财大气粗)的基于实验的综述,试图帮助研究者们「拨开 ...

  9. FCGF论文阅读笔记

    FCGF论文阅读笔记 0. Abstract 从三维点云或者扫描帧中提取出几何特征是许多任务例如配准,场景重建等的第一步.现有的领先的方法都是将low-level的特征作为输入,或者在有限的感受野上提 ...

  10. STM32H7 DMA阅读笔记

    DMA阅读笔记 DMA 的主要特性 DMA 功能描述 FIFO FIFO flush Direct Mode DMA 传输 Peripheral-to-memory mode Memory-to-pe ...

最新文章

  1. 都在抢论文第一作者,怎么解决?
  2. pyvmomi 实现VMware自动化
  3. SQL pivot与unpivot 实现的简单的:行转列及列转行
  4. word里的多级列表和项目编号是什么区别?
  5. Java经典面试题整理及答案详解(三)
  6. StringBuilder内存碎片对性能的影响
  7. 谈操作系统的碎片化和融合
  8. SQL Server-数据类型(七)
  9. rm: 无法删除swap: 不允许的操作_safe-rm老板再也不用担心我删库跑路啦[视频]
  10. js与C#服务端 json数据交互
  11. Mr.J-- jQuery学习笔记(二十二)--入口函数
  12. 03. 数组中重复的数字
  13. keepalive高可用nginx(nginx动静分离)的实现
  14. JS魔法堂:那些困扰你的DOM集合类型
  15. leetcode 77.组合 dfs解法
  16. (转)霍华德·马克斯:警惕那些人人都追捧的事物
  17. MSF(Metaspolit)复现MS12-020蓝屏漏洞
  18. Arcgis如何使用三调数据统计土地三大类
  19. Diamond软件的使用(5)--建立Modelsim仿真环境
  20. Java中级软件工程师应该具备的知识点

热门文章

  1. 用SQL语句创建Access表
  2. 流程 - 发布【敏捷方法之Scrum v0.2.pdf】
  3. iOS初级开发学习笔记:APP生命周期的学习总结
  4. oracle 数据库启动停止小结
  5. 2.5. SciTE
  6. 学习dubbo遇到的问题1
  7. 最全金蝶KIS记账王使用问题汇总
  8. Jquery.ajax(……)
  9. 基于HAProxy的网站架构
  10. Oracle 默认表空间(default permanent tablespace) 说明