【问题标题】:Is it possible for a class to explicitly invoke a method on a protocol's extension?类是否可以显式调用协议扩展上的方法?
【发布时间】:2017-04-11 19:14:40
【问题描述】:

假设有以下协议,其中包含由扩展提供的 someFuncWithDefaultImplementation() 的默认实现。

那么 MyClass2 是否可以同时提供自己的 someFuncWithDefaultImplementation() 实现,该实现还从扩展中调用该方法的默认实现?

protocol MyProtocol : class {
    func someFuncWithDefaultImplementation()
    func someFunc()
    var  someInt:Int { get set }
}

extension MyProtocol {
    func someFuncWithDefaultImplementation() {
        someInt = 5
    }

    func someFunc() {
        someFuncWithDefaultImplementation()
    }
}

class MyClass :  MyProtocol {
    var someInt = 6
}

class MyClass2 : MyProtocol
{
    var someInt: Int = 4
    func someFuncWithDefaultImplementation()
    {
        // do some additional stuff
         /*** someFuncWithDefaultImplementation()  invoke MyProtocol extension implementation here ***/
    }
}


    let class1 = MyClass()
    class1.someFunc()

    let class2 = MyClass2()
    class2.someFunc()

【问题讨论】:

  • @MrSaturn - 在您的副本中尝试了该解决方案,但在这种情况下似乎不起作用。
  • @TusharSharma 不理解您的评论。哪里有覆盖代码中现有功能的扩展?有一个扩展提供了默认实现,它在哪里覆盖了功能?
  • 确保查看链接的欺骗线程中的the (non-elegant) answer using a nested dummy type(例如,someFuncWithDefaultImplementation() 的正文中的class Dummy : MyProtocol { var someInt: Int = 0 }Dummy().someFuncWithDefaultImplementation() MyClass2 以上)。
  • OP:再次查看由 MrSaturn 链接到的线程,特别是关于嵌套虚拟类型的线程。另外,不要在意每个人都在谈论覆盖,因为他们将协议默认实现与为符合子类化而提供。

标签: ios swift


【解决方案1】:

线程中的following answer

描述了使用嵌套的虚拟类型从已经提供了自己的蓝图方法实现的类型中访问协议的默认实现。我们可以扩展此方法以允许实际使用MyClass2 实例的(蓝图)属性,但在对MyClass2 已经实现其自定义版本的默认实现的调用中(因此优先于默认版本) .

我们首先查看您的示例的轻量级版本,并为someFuncWithDefaultImplementation() 提供默认实现

protocol MyProtocol : class {
    func someFuncWithDefaultImplementation()
    var someInt: Int { get set }
}

extension MyProtocol {
    func someFuncWithDefaultImplementation() {
        print("Called default impl. Currently, someInt = \(someInt)")
        print("Mutates someInt from within default implementation (0) ...")
        someInt = 0
    }
}

我们在MyClass2someFuncWithDefaultImplementation()的自定义实现中使用非优雅的嵌套类型解决方案,调用后者的默认实现,但在Dummy实例中存储一个引用回MyClass2例如,允许在默认实现调用中使用MyClass2someInt 属性(用于读取和写入),即使这是从Dummy 类型调用的。

class MyClass2 : MyProtocol
{
    var someInt: Int = 42

    func someFuncWithDefaultImplementation()
    {
        // do some additional stuff ...
        print("In MyClass2 implementation, currently someInt = \(someInt)")

        /* Dummy 'MyClass2'-capturing type used to call the default
            implementation of 'MyProtocol', but with read and write 
            access to 'MyClass2':s self:s 'someInt' instance. */
        class Dummy : MyProtocol {
            unowned let myClass2: MyClass2
            init(_ myClass2: MyClass2) { self.myClass2 = myClass2 }
            var someInt: Int {
                get { return myClass2.someInt }
                set { myClass2.someInt = newValue }
            }
        }

        // call default implementation of 'someFuncWithDefaultImplementation'
        // supplying 'self' to read/write access to self.someInt.
        Dummy(self).someFuncWithDefaultImplementation()

        print("Back in MyClass2:s implementation; now someInt = \(someInt)") 
           // 0, woah, mutated in default implementation!
    }
}

let a = MyClass2()
a.someFuncWithDefaultImplementation()
/* In MyClass2 implementation, currently someInt = 42
   Called default impl. Currently, someInt = 42
   Mutates someInt from within default implementation (0) ...
   Back in MyClass2:s implementation; now someInt = 0         */

您也可以选择在函数外部声明嵌套的Dummy,只需将其标记为private,以确保无法从外部MyClass2访问:

class MyClass2 : MyProtocol
{
    var someInt: Int = 42

    /* Dummy 'MyClass2'-capturing type used to call the default
       implementation of 'MyProtocol', but with read and write 
       access to 'MyClass2':s self:s 'someInt' instance. */
    private class Dummy : MyProtocol {
        unowned let myClass2: MyClass2
        init(_ myClass2: MyClass2) { self.myClass2 = myClass2 }
        var someInt: Int {
            get { return myClass2.someInt }
            set { myClass2.someInt = newValue }
        }
    }

    func someFuncWithDefaultImplementation()
    {
        // do some additional stuff ...
        print("In MyClass2 implementation, currently someInt = \(someInt)")

        // call default implementation of 'someFuncWithDefaultImplementation'
        // supplying 'self' to read/write access to self.someInt.
        Dummy(self).someFuncWithDefaultImplementation()

        print("Back in MyClass2:s implementation; now someInt = \(someInt)") 
           // 0, woah, mutated in default implementation!
    }
}

但是,我将重复链接答案的作者:这种方法不是很优雅。

【讨论】:

  • 是的,我走的是同一条路,but using closures instead :) 我仍然不禁觉得必须有一个更简单的方法——尽管真正的协议默认实现并不意味着是“超级”实现,只有替代实现。在 OP 的实际用例中,这可能通过继承更好地完成(但如果没有看到具体的例子就不可能肯定地说)。
  • @Hamish 我相信,正如您所说,默认实现通常不打算在这种情况下使用,因此(可能;如果您找到更简单的方法,请随时联系我)缺乏简单实现这一目标的方法。整洁地使用闭包并将工作放在协议级别(MyProtocol)而不是单一类型(正如我在上面所做的那样)想要利用这个有点异国情调的功能。现在很晚了@ .se,晚上!
  • 我的意思是,一个明显更简单的方法是在扩展中为默认实现创建另一个方法,并让扩展的someFuncWithDefaultImplementation 链接到它——然后调用默认实现,只需调用具有默认实现的方法:) 但是,除此之外——我想不出另一种不涉及 some 虚拟类型(包括虚拟“超级”协议)的方法。
【解决方案2】:

仅当您也进行子类化时,请参见:

import Foundation

protocol MyProtocol : class {
    func someFuncWithDefaultImplementation()
    func someFunc()
    var  someInt:Int { get set }
}

extension MyProtocol {
    func someFuncWithDefaultImplementation() {
        someInt = 5
        print("a")
    }

    func someFunc() {
        someFuncWithDefaultImplementation()
    }
}

class MyClass :  MyProtocol {
    var someInt = 6
}

class BaseClass: MyProtocol {
    var someInt: Int = 4
}
class MyClass2 : BaseClass
{

    func someFuncWithDefaultImplementation()
    {
        // do some additional stuff
        print("b")
        (self as BaseClass).someFuncWithDefaultImplementation()
    }
}

如果您在 Playground 中添加上述内容,然后调用:

MyClass2().someFuncWithDefaultImplementation()

打印出来:

b
a

你要的是什么,看截图。

【讨论】:

  • 这不起作用。当 MyClass2 从 MyProtocol 继承时,它的 someFuncWithDefaultImplementation() 实现将按预期调用。但是,如果它更改为从 MyClass 继承,则不再是这种情况 - 直接调用协议扩展实现。
  • 这很奇怪,在最新的 Xcode 8.3.1 Playground 中工作(如截图所示)
  • 您是否尝试过在问题中调用它,即 MyClass2().someFunc()
  • 这个问题是它不会使用(协议)动态调度——MyClass2someFuncWithDefaultImplementation() 实现不会从编译器不知道的上下文中调用该实例是MyClass2(例如(MyClass2() as BaseClass).someFuncWithDefaultImplementation())。这是因为subclasses don't get to directly re-implement protocol requirements.
  • 没错,我错了:我的解决方案无法解决调用 someFunc() 的问题,我被问题标题欺骗了。对不起
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多