我想知道一个简单类中初始化程序的协议等价物是什么,它只包含初始化功能,只能在具体类中扩展。
所以最简单的方法就是显示代码 - 我正在寻找与以下内容相当的协议扩展:
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?)但我不确定它们是否有效,也没有具体解决类初始值设定项中其他参数的问题。
答案 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也为你的贡献。