Effective_Objective-C_4协议与分类】

前言

  • 第四章的名称叫协议和分类,这两个词都是之前比较熟悉的老朋友了。协议是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分类隐藏实现细节等等
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值