【问题标题】:Implementing Swift protocol methods in a base class在基类中实现 Swift 协议方法
【发布时间】:2014-11-19 04:25:51
【问题描述】:

我有一个定义如下方法的 Swift 协议:

protocol MyProtocol {

    class func retrieve(id:String) -> Self?
}

我有几个不同的类符合这个协议:

class MyClass1 : MyProtocol { ... }
class MyClass2 : MyProtocol { ... }
class MyClass3 : MyProtocol { ... }

每个子类中retrieve 方法的实现几乎相同。我想将这些函数的通用实现拉到一个符合协议的共享超类中:

class MyBaseClass : MyProtocol
{
    class func retrieve(id:String) -> MyBaseClass?   
}

class MyClass1 : MyBaseClass { ... }
class MyClass2 : MyBaseClass { ... }
class MyClass3 : MyBaseClass { ... }

这种方法的问题是我的协议将retrieve方法的返回类型定义为Self类型,这才是我最终真正想要的。但是,结果我不能以这种方式在基类中实现retrieve,因为它会导致MyClass1MyClass2MyClass3 的编译器错误。这些类中的每一个都必须符合它们从MyBaseClass 继承的协议。但是因为这个方法是用MyBaseClass的返回类型实现的,并且协议要求它是MyClass1,所以它说我的类不符合协议。

我想知道是否有一种干净的方法来实现一个协议方法,该方法在基类中的一个或多个方法中引用Self 类型。我当然可以在基类中实现一个不同名称的方法,然后让每个子类通过调用其超类的方法来实现协议来完成这项工作,但这对我来说似乎不是特别优雅。

我在这里缺少更直接的方法吗?

【问题讨论】:

    标签: swift


    【解决方案1】:

    这应该可行:

    protocol MyProtocol {
        class func retrieve(id:String) -> Self?
    }
    
    class MyBaseClass: MyProtocol {
    
        required init() { }
    
        class func retrieve(id:String) -> Self? {
            return self()
        }
    }
    

    required init() { } 是确保从 MyBaseClass 派生的任何子类具有 init() 初始化器所必需的。

    请注意,此代码会使 Swift Playground 崩溃。我不知道为什么。因此,请尝试实际项目。

    【讨论】:

    • 我将此答案标记为正确答案,因为它确实回答了我提出的问题。但是,我发现它不足以满足我的需求,因为我不太了解其他因素,无法包括在内。我会为有兴趣的人发布我自己的答案,并附上详细信息。
    【解决方案2】:

    仅通过您的示例不确定您要在这里完成什么,所以这是一个可能的解决方案:

    protocol a : class {
      func retrieve(id: String) -> a?
    }
    
    class b : a {
      func retrieve(id: String) -> a? {
        return self
      }
    }
    

    背后的原因

    protocol a : class
    

    声明使得只有 reference 类型可以是扩展。在处理类时,您可能不想传递值类型(结构)。

    【讨论】:

    • 这并不完全符合我的要求,因为它告诉 Swift 我正在返回一个实现协议“a”的对象,而不是返回一个作为 b 类实例的对象。此外,它不允许我实现一个名为“c”的“b”子类并让检索方法返回一个“c”的实例
    【解决方案3】:

    我已将@rintaro 的答案标记为正确答案,因为它确实回答了我提出的问题。但是,我发现此解决方案过于局限,因此我发布了我发现的替代答案,以供遇到此问题的任何其他人使用。

    上一个答案的局限性在于,它仅在Self 表示的类型(在我的示例中为MyClass1MyClass2MyClass3)在协议中使用或作为从类方法返回类型。所以当我有这个方法时

    class func retrieve(id:String) -> Self?
    

    一切都如我所愿。然而,当我完成这个工作时,我意识到这个方法现在需要是异步的,并且不能直接返回结果。所以我用类方法尝试了这个:

    class func retrieve(id:String, successCallback:(Self) -> (), failureCallback:(NSError) -> ())
    

    我可以将此方法放入MyProtocol,但是当我尝试在MyBaseClass 中实现时,我收到以下编译器错误:

    Error:(57, 36) 'Self' is only available in a protocol or as the result of a class method; did you mean 'MyBaseClass'?
    

    所以我真的不能使用这种方法,除非Self引用的类型以非常特定的方式使用。

    经过一些实验和大量的 SO 研究,我终于能够使用泛型让一些东西更好地工作。我在协议中定义的方法如下:

    class func retrieve(id:String, successCallback:(Self) -> (), failureCallback:(NSError) -> ())
    

    然后在我的基类中执行以下操作:

    class MyBaseClass : MyProtocol {
        class func retrieve<T:MyBaseClass>(id:String, successCallback: (T) -> (), failureCallback: (NSError) -> ()) {
            // Perform retrieve logic and on success invoke successCallback with an object of type `T`
        }
    }
    

    当我想检索MyClass1 类型的实例时,我执行以下操作:

    class MyClass1 : MyBaseClass {    
        func success(result:MyClass1} {
            ...
        }
        func failure(error:NSError) {
            ...
        }
        class func doSomething {
            MyClass1.retrieve("objectID", successCallback:success, failureCallback:failure)
        }
    

    通过这个实现,success 的函数类型告诉编译器在MyBaseClass 中的retrieve 的实现中应该为T 应用什么类型。

    【讨论】:

      猜你喜欢
      • 2017-03-19
      • 2015-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-02
      • 2015-01-01
      • 2017-11-25
      • 1970-01-01
      相关资源
      最近更新 更多