解读协议(Protocol)

作者: 查无此人123 | 来源:发表于2016-08-20 15:28 被阅读1012次

    写在前面

    • 我是这次看文档是直接从delegate下的手,因为委托回调与代理的区别一直没搞明白,结果我看完了文档,发现我更迷茫了,上网查了各种中文的,英文的,结果是,依然迷茫.....脑中一万个fuck飘过~于是,我放弃了,去啃剩下的部分,在这整个过程中,因为有些内容不明白,不熟悉,又跳到别的区域查寻相关内容,了解了Class章节中的Type Method,又因为Type Method的一些内容,我跳跃到Initializer章节,了解了Failable Initializer,结果又因为Failable Initializer里面提到了其在Enumeration中的应用,我又跳跃到了Enumeration到此打住,以前我很讨厌这种,菊花链一般的一个知识连一个知识的方式,不过这次我尽然找到了乐趣.我想了一下,这可能跟我,把每个章节的小标题都单独摘出来有关,让我在进行菊花链学习之前,已经根据这个小标题的索引大概对应到自己身上,知道哪些熟悉,哪些陌生,所以当我从一个菊花跳到另一个章节的时候,不会因为觉得又有一整章内容,而害怕,因为,我看的到小标题,我知道,我这次通过上个菊花跳过来只需要了解这一小节前后的知识,就可以搞定,然后调回去,所以,没有心里压力,哈哈,总结完毕,开始正题~

    • 主要分五大块讲的:

      协议的定义 , 语法 , 声明与实现的要求 , 协议是一种类型 , 在代理中的应用

    • 文中有好几个纠结我的问题,期待有大神的解答...

    定义

    一句话定义: 协议是方法声明的集合

    详细解释:

    swift里的协议没有现实生活中的那么复杂,这里的协议就是声明了一堆方法(计算属性也算),这些方法是我想找人来帮我做的事,有些事我不求结果,有些事情我希望你做完了以后,给我一个反馈,这个反馈结果可能是要确定ok还是不ok,也可能我要一个确定的东西(字符串,字符,数字,或是某个类的对象,某个结构体的对象,某个枚举的对象,也可能是一个函数)

    补充: protocol 可以被类(class),结构体(structure),枚举(enumeration)遵循.

    语法

    声明语法

    protocol SomeProtocol: class { /* ..省略.. */ }
    

    定义了一个只允许class遵循的协议,名字叫做SomeProtocol.也可以写成只允许结构体或者枚举遵循,只需在协议名后写: struct : enum

    遵循语法

    class SomeClass: SomeSuperclass, FirstProtocol,  AnotherProtocol {  /*..省略..*/ }
    

    可以看到,除了写上遵循协议以外,还同时可以写上父类,但是这里要注意,父类必须写在协议的前面.

    协议中的声明与实现的要求

    属性声明与实现的要求

    • 声明: 两种{get set}{get} , 看起来有点像计算属性,代码如下:

      protocol someProtocol {
        var property1: String {get set}
        var property2: String {get}
      }
      
    • 实现: 声明的时候看起来像计算属性,其实实现的时候不一定是计算属性,也可以是存储属性,你只需要根据声明的需求看,是需要只读属性,还是需要可读又可写的属性,甚至,协议里声明只读属性,你也可以给它实现成可读可写的属性,因为可读又可写的属性已经满足可读这个条件

    方法声明与实现的要求

    • 定义时: 当你定义的协议的方法会涉及到要更改遵循它的类型自身的属性的时候,就需要在方法前面加上关键词mutating
    • 实现时: 当你实现你定义的mutating方法时,就要分两种情况,因为class天生就允许你对它自身的属性进行更改,给classmutating实现方法的时候就不需要写mutatitng关键字,因为这属于多此一举(也许class的底层就已经帮你用了这个关键词),而涉及到sturctunumeration的时候,你实现方法的时候还是需要加关键词的,因为它们天生不允许修改自身的属性.
    • 补充:
      • 涉及到声明类型方法的时候,就用关键词static,这个关键词已经包含了实际实现中classstatic两种情况
      • 除了声明方法和声明属性,还可以声明初始化器initializer,不过, 既然骆神说在协议里定义初始化方法,是一种很丑陋的做法,那我就暂时不关注了,等用到的时候再说吧~~哈哈(偷个懒)

    协议也是一种类型

    protocol也是一种类型(首字母大写)

    • 可以作为函数,方法的参数,返回值
    • 可以作为属性,变量,常量
    • 可以作为数组,字典等容器的元素,也就是说可以创造一个由各种协议组成的容器类型(array,dictionary,set)

    既然协议是一种类型,那么,我们就可以对它进行扩展,继承,和一些运算操作

    下面就针对这三点说一下,协议在做相应操作时的一些独特性质

    • 扩展:

      • 如果某个类型已经实现了某个协议的需求,但是它并没有作出声明,那我们可以利用extension在它后面给补一个声明,代码如下

        extension someType: someProtocol {}
        

        然后我们就可以把这个SomeType应用与那些需要someProtocol协议的场景了.(因为swift里,如果你不声明,类自己是不会自动去遵循某个协议的)

      • 可以在协议扩展中给协议中的方法提供默认实现,也就是说如果某个类遵循了协议但没有实现这个方法,swift就会直接使用这个默认实现,那么这个方法也就相当于是一个可选方法(可以实现也可以不实现)

      • 协议的扩展还可以添加约束,通过wherer从句,这个在泛类型里提到过,就不说了

    • 继承:

      • 多重继承: 一个协议可以继承一个协议或多个协议

      • class-only: 这个也算不上是纯继承的内容,开头有提到过,

        protocol SomeProtocol: class { /* ..省略.. */ }
        

        就是让这个协议只允许类来遵循,不允许struct与enumeration遵循

    • 运算操作:

      • 协议的组合: 把多个协议合成一个协议

        protocol<someProtocl, AnotherProtocol>
        

        这里的协议组合(Protocol Composition)并没有定义一个新的协议,而是一个暂时的组合

        疑惑: 意思是说这个这个暂时的协议组合与正常建立的协议应该是在作用域上有差别吗?还是说不能拿来继承什么的?

      • 协议的判断与转换:

        • is : 可以通过is来检查某对象是不是遵循某协议
        • as? : 可以通过as? 来解包,查看是否遵循某协议,一般可结合if来使用
        • as! : 当你十分确信它遵循某协议,可用这个来转换(算是某种意义上的强制转换吧,总感觉应该是生成了一个新的对象而已),但也要注意,这样用会有报错的风险.

    协议在代理中的运用

    骆神为了以防将来我们装X失败,多次强调这个委托回调代理是完全不同的东西,然后告诉我们iOS中大量运用的都是委托回调,很荣以弄混,并举了几个例子来给我解释,但我表示并没有弄弄明白.

    我去网上寻找相关的内容有以下几点疑问:

    1. 委托回调的英文说法是否就是Call Back,不是,那我表示我到目前为止我的寻找方向就是个错误,因为中文区根本搜不到相关内容,用英文模糊搜索,才找到疑似相关内容的东东(diferent between Delegate and Call Back ),这是我检索所用的关键词,如果是的话,那我要说第二条了
    2. 洛神也提到了说代理模式是"四人帮"设计模式的一种,但我表示,我上维基百科上看了一下相关模式,并没有找到类似的模式,请知道的指条明路,主要是因为想了解这两者的区别
    3. 我根据第一条说的关键词,检索出来一些人提出的疑似与我相同疑问的问题,我看到了下面几种回答
      • A delegate is the description on how the callback function looks like
      • 如果一个method或function的调用,只有单一的callback,那么就用block,如果可能会有多个不同的callback,那么就使用delegate.这么做的好处是,当一个method或function调用时会有多种callback时,有可能某些callback是没有必要实现的
      • Generically, a delegate is an object used to access a method external to the object owning the method, while a callback is a variable that holds a delegate
      • Delegates are the idiomatic way of implementing callbacks in .NET - but you don't have to.There are plenty of uses for delegates beyond callbacks in .NET - it depends on exactly what you deem to be a callback, but GUI event handlers, thread-starters, filters and projections (and more!) in LINQ to Objects all use delegates.
      • A "callback" is a term that refers to a coding design pattern. In this pattern, you pass a pointer to a function to another function, so that within the called function, it can "call back" the function you passed to it. A Delegate, otoh, is a specific .Net type that acts as a signature-specific container for a function pointer...

    晕了吧~我早就晕了,我根本搞就是脑子早就炸了...本来英语就差,结果你一嘴,我一嘴,完全搞不懂...

    我再描述一下我对洛神对这个问题的描述与解释的理解吧,大家可以参考一下,明白的可以出来冒个泡,给哥们儿指条明路

    场景: 学生找枪手代考

    • 委托回调的实现方法:
      1. 声明一个枪手代考的协议
      2. 在学生类里定义一个delegate属性,该属性遵循枪手代考协议,且是个可空类型,且使用weak关键词定义,以防循环调用造成的内存泄漏
      3. 在学生考试这个方法中,把delegate属性拿出来,也就是把枪手找来,让枪手执行枪手自带的代考方法
      4. 枪手类这边,要声明自己遵循了代考协议
      5. 枪手类在自己的代码里,要实现代考协议要求的代考方法
      6. 在现实中(在main函数中),把枪手赋值给学生的delegate属性,并让学生调用考试方法,学生的考试方法中自然就会激活枪手去调用枪手自己的代考方法,于是代考事件,发生了
    • 代理的实现方法:
      1. 声明一个考试协议
      2. 学生与枪手都声明,且实现了该考试协议
      3. 枪手类设置了一个target属性,该属性是学生类,
      4. 枪手在实现自己的考试协议要求的考试方法中,调用了这个target属性,并取到了学生的姓名,年龄,考号,然后开始代考

    好了,委托回调和代理方法的实现都描述完毕了,请问这区别在哪?我知道这有很多缺别,请问它们本质的区别在哪?我今后怎么区分?我又应该如何运用呢?

    下面也贴出我的委托回调的笔记

    委托回调

    有的时候某个对象做某件事但其自身又没有能力做这件事,这个时候就可以使用委托回调的编程模式让别的对象来做这件事

    实现委托回调编程模式步骤:

    1. 设计一个协议(被委托方必须要遵循协议才能给别的对象当委托)
    2. 委托方添加一个属性,其类型是被委托方(抽象的)
    3. 自己做不了的事,委托给别的对象来做
    4. 让视图控制器遵循协议,称为被委托方(协议表能力)
    5. 遵循协议要实现协议中的方法(协议表约定)
    6. 给画布对象绑定委托
    委托回调Tips

    在定义协议的时候可以通过如下方法,限制该协议只允许类调用(或结构体,枚举都可以)

    protocol CanvasDelegate: class {
      //...省略...
    }
    

    同时,如果限制只能类调用的话,那么在第二步,委托方添加属性的时候,需要在前面添加关键词weak,是为了防止委托方与被委托方形成闭环后导致内存无法释放的情况,所以一般都会在前面加上关键词weak

    我再说一点,我在这个过程中了解到的东西,就是为什么要用协议,而不用继承,

    继承为类型引入的是静态特质,也就是说继承而来的功能都是在编译的时候获取的

    而协议则不同,声明协议的时候并没有实现,而是谁调用谁实现,是一种动态的往一个类中添加新的行为的方式,这样的方式比生成子类更为灵活

    其实这里我也有个疑问:

    灵活在哪? 是因为编译的时候就生成一堆子类会占用很多空间吗?

    相关文章

      网友评论

        本文标题:解读协议(Protocol)

        本文链接:https://www.haomeiwen.com/subject/fcjisttx.html