【Effective Objective-C】—— 协议与分类

      最后更新:2022-01-21 07:54:29 手机定位技术交流文章

      第23条:通过委托与数据源协议进行对象间通信

      1.委托模式:

      “委托模式”也就是我们通常说的代理,它是用来实现对象间的通信的,也就是传递数据。
      它的主旨是:定义一套接口,某对象若想接受另一个对象的委托,则需遵从此接口,以便成为其“委托对象”。而这“另一个对象”则可以给其委托对象回传一些信息,也可以在发生相关事件时通知委托对象。
      想必大家都很清楚委托了,这里也就不再过多赘述了。但是我们一定要注意协议的修饰符,这个属性需定义成weak,而非strong,因为两者之间必须为“非拥有关系”。如果使用strong,它将会成为“拥有关系”从而会引入“保留环”。

      实现委托对象的办法是声明某个类遵从委托协议,然后把协议中想实现的那些方法在类中实现出来。如果要向外界公布此类实现了某协议,那么就在接口处声明,而如果这个协议是个委托协议的话,那么通常只会在类的内部使用。也就是在.m文件中遵循协议。
      432423

      如果要在委托上调用可选方法,那么必须提前使用类型信息查询方法判断这个委托对象能否响应相应选择子,也就是说看这个协议是否实现了那个方法:5435345

      通常我们在写协议时,没有那种把委托实例也一起写入方法中的习惯,但是我们若是把委托实例也一并写入,那么delegate对象在实现相关方法时,就能根据传入的实例分别执行不同的代码了。就比如:34534

      2.委托的两种情况:

      委托模式通常有两种情况,一种是数据和类之间的委托,将获取的数据传给类,这就是“数据源模式”,还有一种就是我们通常的“委托模式”了,对象把应对某个行为的责任委托给另外一个类了。
      23423423
      但是,在我们实现委托模式和数据源模式的时候,如果协议中的方法时可以选的,那么就会写出一大堆下面的代码:4234234
      如果这种代码过多,将会大大影响程序的运行效率,所以这里我们就可以用到C语言中乏人问津的“位段”数据类型了,使用这种数据类型将方法的相应能力缓存起来,在进行判断的时候只需要将缓存调出判断即可,就很方便。4324
      312312
      我们可以将其缓存写在delegate属性所对应的方法中,那么这样每次设置代理的时候它就会自行进行判断和缓存:4234234
      这样就少去了每次进行的是否响应某方法的判断了。

      3.要点:

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

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

      我们都知道OC中有一个分类机制,但是我们通常是使用它来补充一个我们需要的类,但是他其实还有更好的用途,那就是用来规划我们的代码,我们可以通过OC的“分类”机制,把类代码按逻辑划入几个分区中。就像这样:423423
      我们使用了Friendship、Work和Play三个分类,将我们自己定义的方法进行了分类,这样看上去代码就十分的整齐,便于规划。添加他们的头文件时,就可以这样进行相应的添加:45345
      这就是它们相应的头文件,实现部分就和通常的差不多,有什么实现什么就行了。234234
      通过分类机制,可以把类代码分成多个易于管理的小块,以便单独检视。虽然它引入文件时会稍微有点麻烦,但是他还是一种管理代码的好方法,并且在调试程序的时候它还会明确的指出相应的分类,这就对我们调试程序有很大的帮助,所以说使用分类来规划代码的利大于弊。

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

      要点:

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

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

      分类机制通常用于向无源码的既有类中新增功能,但是他也存在相应的问题:分类中的方法是直接添加在类里面的,他们就好比这个类中的固有方法。将分类方法加入类中这一操作是在运行期系统加载分类时完成的。运行期系统会把分类中所实现的每个方法都加入类的方法列表中。如果类中本来就有此方法,而分类又实现了一次,那么分类中的方法就会覆盖原来的那一份实现代码。并且多次覆盖的结果以最后一个分类为准。

      所以,我们要想解决上述的问题,就必须在命名上下功夫,只要让他们不重名不就覆盖不了了,就和之前说的命名方法一样,我们在相关的名称都加上某个共用的前缀456456

      这样做虽然不能百分百的确定不会出现覆盖情况,但是这样就大大减少了覆盖的概率。并且在调试的时候也可以清楚的看到究竟是哪个分类出了问题。

      要点:

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

      第26条:勿在分类中声明属性

      除了“class-continuation”分类之外,其他分类都无法向类中新增实例变量,因此,他们无法把实现属性所需的实例变量合成出来。因为分类中无法自动合成属性的实例变量,所以你要是想在分类中实现属性就得自己进行设置该属性的存取方法。你就可以用@dynamic或者消息转发机制来实现其存取方法,但是还是不太提倡那样做的。

      当然,除了上述的方法可以实现设置分类中属性的存取方法之外,还有关联对象也可以解决在分类中不能合成实例变量的问题。就像这样:
      4324234234
      这样做可行,但是不太理想,要把相似的代码写很多遍,而且内存管理问题上容易出错,因为我们在为属性实现存取方法时,经常会忘记遵从其内存管理语义。

      我们正确的做法应该是:把所有属性定义在主接口里。类所封装的全部数据都应该定义在主接口中,这是唯一能够实现实例变量的地方。并且分类机制,它的目的就是用于扩展类的功能,而非封装数据。

      有时属性可读也可以定义在分类中,我们可以用其get方法获取相关的信息,但是这样的话还不如直接定义一个方法多好,用处还多,所以说,分类还是好好使用其扩展功能吧。

      要点:

      • 把封装数据所用的全部属性都定义在主接口里。
      • 在“class-continuation分类”之外的其他分类中,可以定义存取方法,但尽量不要定义属性。

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

      1.class-continuation分类:

      OC动态消息系统的工作方式决定了其不可能实现真正的私有方法或者私有实例变量。那么怎么实现私有变量和私有方法呢?这就要用到特殊的“class-continuation分类”了。

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

      2.“class-continuation分类”的合理用法:

      “class-continuation分类”还有一种合理用法,就是将public接口中声明为“只读”的属性扩展为“可读写”,以便在类的内部设置其值。

      就是说,你在外部.h文件中定义一个“只读”的属性,然后你又在“class-continuation分类”将其的“只读”属性改为“可读写”的,那么这样下来,在外部看来他就是一个“只读”的属性,但是你可以在其内部自定义的设置其值了,他在内部来说就是“可读写”的了。

      这样做很有用,既能令外界无法修改对象,又能在其内部按照需要管理其数据。这样,封装在类中的数据就由实例本身来控制,而外部代码则无法修改其值。

      还有一种用法:若对象所遵从的协议只应视为私有,则可在“class-continuation分类”中声明名,这样就不会泄漏我们所遵从的协议了。4234234

      3.要点:

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

      第28条:通过协议提供匿名对象

      1.匿名对象:

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

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

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

      • 先定义一个协议其中包括数据库都有的方法:
        5343453
      • 提供一个单例接口:
        45435
        这样的话,处理数据库连接的类名称就不会暴露了,来自不同框架的那些类限制就都可以使用同一个方法来返回了,而不用对每个类都写一个这种协议。

      又例如:4234234234
      这里的sectionInfo就是一个匿名对象,你并不知道他返回的究竟是哪个类的对象,你只知道他们都能调用的方法和属性。

      2.要点:

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

      本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/16991.html

          热门文章

          文章分类