2016-01-14 193 views
10

我想知道一个简单的类中的初始化程序等同于什么协议,它只包含初始化功能,并且仅用于在具体类中扩展。协议扩展初始化程序

所以可能是最简单的就是显示代码 - 我要找的协议扩展相当于如下:

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?)中提出的解决方案,但我不确定它们是否工作,也没有专门解决类初始化程序中的其他参数的这个问题。

回答

11

你必须提供一个有效的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

符合AnyObject我们多余的,隐式解包的名称和颜色是危险的,而不是必需的(改为设置默认值) – user3441734

+0

感谢您的富有洞察力的评论,您似乎遇到了类似的障碍。我同意你的评论是否值得这样做。我想我正在寻找在面向协议的方法中实现上述问题的最佳实践。需要给vars默认值或者让它们隐式解开对我来说似乎不是太方便也不是最好的做法,并且让我怀疑这是否使用协议的味道对于这个问题不是正确的方法。 @ alain-t你可以使用代码示例填写你的评论'代表班'吗? –

+0

@CraigGrummitt swift中的所有变量(和/或常量)在使用之前必须有一些值(对于引用类型,Swift中的变量表示为类,值为引用,对于值类型,它是值self)。直接相当于null不存在的就是Swift。 var i =可选()具有默认值nil。 var j = ImplicitlyUnwrappedOptional ()给出了相同的结果(两者都是枚举'类型')....(继续下面) – user3441734

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)") 
+0

得到我的头后,你的协议名称中有'class'这个词,我按照你的例子,谢谢你的输入。用另一种方式提出你的建议,你使用工厂类来建立原来简单的'Thing'类中的任何属性吧?我想我把它作为这个问题的解决方案的问题是,它的基础上它再次使用一个类。但这是一个有趣的例子,谢谢。 –

+0

它确实使用了一个类,但是这里的好处是你可以在继承层次结构之外使用它,就像使用协议一样。我正在使用它来实现“多重继承”,这就是为什么我使用Class来命名协议的原因。对于单个变量,它看起来像是一种矫枉过正,但是当您想要将数据和行为添加到具有自己层次结构的多个类时,它开始变得更有意义。为了您的具体需求,我同意这可能不是最好的方法。 –

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

真棒,没有问题。

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

不,这是行不通的。这打破了太多规则。首先也是最重要的是,这不一定会设置所有的属性。考虑你的NamedThing。这种情况下name是什么?如果color设置程序获取其他尚未设置的属性,会发生什么情况?编译器看不到每个可能的实现,所以它不知道是否color只是一个伊娃或更复杂的东西。不,这不起作用。

真正的问题是“可以在具体类中扩展的抽象类”。忘记课程。忘记继承。 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) 
    } 
} 

因为你的扩展点是处理部分初始化,就让它计算出您需要的部分。不要试图在扩展中使它成为初始化器,因为它必须负责初始化所有内容,并且在将它与继承结合时非常难以正确执行。

+0

感谢您的帮助Rob。为简单起见,我将代码简化为一个简单的例子,而没有深入讨论为什么它需要成为一个类,但在现实生活中,它是一个需要实现协议的类,因为它将继承一个UIKit类。 样板代码旨在通过处理颜色设置的协议扩展来说明。 –

+0

顺便说一句,我刚刚通过您的评论意识到“Swift中没有抽象类”,我们可能正在处理对“抽象类”的不同理解,如果我把这个问题与对“抽象类”的错误理解混淆了,术语。我编辑的问题更清晰。 –

+0

这个UIKit类是什么总是必须被子类化? (我们同意什么是抽象类,我只是想不出你正在谈论的类。UIGestureRecognizer?)我强烈怀疑你面临的实际问题以另一种方式更容易解决。这是过度简化问题的经典问题。 –

-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()) 

谢谢@阿兰-T的答案我我会接受,因为它最接近我的问题的解决方案,尽管它包括隐含的解包属性。

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