协议扩展初始化程序

时间:2016-01-14 23:01:12

标签: swift initialization swift2 protocols

我想知道一个简单类中初始化程序的协议等价物是什么,它只包含初始化功能,只能在具体类中扩展。

所以最简单的方法就是显示代码 - 我正在寻找与以下内容相当的协议扩展:

import UIKit

class Thing {
    var color:UIColor
    init(color:UIColor) {
        self.color = color
    }
}
class NamedThing:Thing {
    var name:String
    init(name:String,color:UIColor) {
        self.name = name
        super.init(color:color)
    }
}
var namedThing = NamedThing(name: "thing", color: UIColor.blueColor())

我期待代码看起来像:

protocol Thing {
    var color:UIColor {get set}
}
extension Thing {
    init(color:UIColor) {
        self.color = color
    }
}
class NamedThing:Thing {
    var name:String
    var color:UIColor
    init(name:String,color:UIColor) {
        self.name = name
        self.init(color:color)
    }
}

我已经看到其他StackOverflow问题中提出的解决方案(例如How to define initializers in a protocol extension?)但我不确定它们是否有效,也没有具体解决类初始值设定项中其他参数的问题。

4 个答案:

答案 0 :(得分:13)

您必须提供一个有效的init链来创建类的实例,并限制协议中初始化程序的选项。

由于您的协议无法确定涵盖使用它的类的所有成员,因此您在协议中声明的任何初始化程序都需要将类的“未知”成员的初始化委托给该类提供的另一个初始化程序。本身。

我调整了您的示例,使用基本的init()作为协议的委托初始化程序来说明这一点。

如您所见,这要求您的类在调用init()时为所有成员实现初始值。在这种情况下,我通过在每个成员的声明中提供默认值来做到这一点。并且,由于某些成员并不总是有实际的初始值,我将其更改为自动解包选项。

为了使思考更有趣,你的类不能将初始化委托给协议提供的初始化器,除非它通过一个方便的初始化器。

我想知道所有这些限制是否值得。我怀疑你正在尝试使用协议,因为你需要在实现协议的类之间一致地初始化一堆公共变量。也许使用委托类可以提供比协议更简单的解决方案(只是一个想法)。

protocol Thing:AnyObject
{
    var color:UIColor! { get set }
    init()
}

extension Thing 
{    
    init(color:UIColor)
    {  
       self.init()
       self.color = color
    }
}

class NamedThing:Thing 
{
    var name:String!   = nil
    var color:UIColor! = nil

    required init() {}

    convenience init(name:String,color:UIColor) 
    {
        self.init(color:color)
        self.name = name
    }
}

答案 1 :(得分:13)

protocol Thing {
    var color: UIColor {get set}
}

太棒了,没有问题。

extension Thing {
    init(color: UIColor) {
        self.color = color
    }
}

没有。这永远不会奏效。这打破了太多规则。第一个也是最重要的是,这并不一定要设置所有属性。考虑一下您的NamedThing。在这种情况下,name是什么?如果color setter获取尚未设置的其他属性,会发生什么?编译器还无法看到每个可能的实现,所以它不知道color只是一个ivar还是更复杂的东西。不,这不会起作用。

真正的问题是"一个可以在具体类中扩展的抽象类。"忘记课程。忘记继承。 Swift完全是关于组合和协议,而不是继承。

因此,让我们考虑一下您在评论中描述的示例(尽管在Cocoa中,没有"抽象类,"或者)。我们假设设置颜色实际上是很多你不想复制的代码。这没问题。你只需要一个功能。

import UIKit

protocol Thing {
    var color: UIColor {get set}
}

private extension Thing {
    static func colorForColor(color: UIColor) -> UIColor {
        // We don't really use the color directly. We have some complicated code that we don't want to repeat
        return color
    }
}

final class NamedThing: Thing {
    var name: String
    var color: UIColor

    init(name: String, color: UIColor) {
        self.name = name
        self.color = NamedThing.colorForColor(color)
    }
}

由于扩展的目的是处理部分初始化,只需让它计算你需要的部分。不要试图让它成为扩展中的初始化器,因为它必须负责初始化所有内容,当你将它与继承混合时,这很难正确地完成。

答案 2 :(得分:1)

这就是我对“代表班”的想法。

这是我用来使用协议将存储的变量添加到类中的技术。

class ManagedColors
{
   var color:UIColor
   // other related variables that need a common initialisation
   // ...
   init(color:UIColor)
   {
      self.color = color
      // common initialisations for the other variables
   }
}

protocol ManagedColorClass
{
    var managedColors:ManagedColors { get }
}

extension ManagedColorClass 
{    
    // makes properties of the delegate class accessible as if they belonged to the
    // class that uses the protocol
    var color:UIColor { 
                        get { return managedColors.color } 
                        set { managedColors.color = newValue }
                      }    
}


// NamedThing objects will be able to use .color as if it had been
// declared as a variable of the class
//
// if you add more properties to ManagedColors (and the ManagedColorHost protocol)
// all your classes will inherit the properties as if you had inherited from them through a superclass
// 
// This is an indirect way to achive multiple inheritance, or add additional STORED variables with
// a protocol
//
class NamedThing:ManagedColorClass 
{
    var name:String
    var managedColors:ManagedColors 

    init(name:String,color:UIColor) 
    {
        managedColors = ManagedColors(color:color)
        self.name = name
    }
}

let red = NamedThing(name:"red", color:UIColor.redColor())
print(" \(red.name)  \(red.color)")

答案 3 :(得分:-1)

我得出的结论是,这个问题有点无法解决,因为协议扩展无法动态定义属性(除非它为属性提供默认值,或者将它们声明为隐式解包)。要以更加面向协议的方式解决这个问题,它需要一种不同的方法,它仍然涉及声明和初始化具体类中的所有变量,如:

import UIKit
protocol Colorable {
    var color: UIColor {get set}
}
protocol Nameable {
    var name: String {get set}
}
class ColoredNamedThing: Colorable, Nameable {
    var name: String
    var color: UIColor

    init(name: String, color: UIColor) {
        self.name = name
        self.color = color
    }
}

var coloredNamedThing = ColoredNamedThing(name: "Name", color: UIColor.redColor())

感谢@ alain-t接受我接受的答案,因为它最接近我的问题的解决方案,尽管它包括隐式展开的属性。

感谢@ rob-napier也为你的贡献。