2015-05-28 26 views
1

在使用大量初始化工具覆盖的子类中使用常量是很乏味的。看看下面的类,我需要在两个初始化器中复制代码。使用常量时的常见初始化

class Test : UIView { 

    let subview: UIView 

    override init(frame: CGRect) { 
     subview = UIView() // once 
     super.init(frame: frame) 
    } 

    required init(coder aDecoder: NSCoder) { 
     subview = UIView() // twice 
     super.init(coder: aDecoder) 
    } 

} 

如果我试图用一个共同的初始化的话,我得到以下错误(见注释)

override init(frame: CGRect) { 
    commonInit() // 1: Use of 'self' in method call 'commonInit' before super.init initializes self 
    super.init(frame: frame) // 2: Property 'self.subview' is not in initialized at super.init call 
} 

private func commonInit() { 
    subview = UIView() // 3: Cannot assign to 'subview' in 'self' 
} 

它工作正常,如果我不使用一个常数,这样定义子视图:在初始化

var subview: UIView? 

然后当然开关顺序是这样的:

super.init(frame: frame) 
commonInit() 

所以我的问题是:有没有办法使用常用的常量初始化Swift中的常量?

编辑:我完全忘了提的是,这里的斗争,我不能启动子视图之前,我在init是,它是基于声明的常量时不知道数据开始。

+0

重复http://stackoverflow.com/questions/的24023412/how-to-implement -inits-with-same-content-without-code-duplicate-in-swift? –

+0

不,正如我所说我知道如何为var做这个,我使用let – Johannes

+0

请参阅下面的答案。它允许您根据上下文正确地初始化值。 –

回答

0

执行以下操作:

class Test : UIView { 

    let subview = UIView() 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     //edit subview properties as needed 
    } 
} 
2

试试这个:

class Test : UIView { 

    let subview = UIView() 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
    } 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 
} 
0

一种选择是继的Xcode模式:

class Test : UIView { 

    var subview: UIView! 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     commonInit() 
    } 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     commonInit() 
    } 

    func commonInit() { 
     subview = UIView() 
    } 
} 

通知您subviewvar

另一种选择是:

class Test : UIView { 

    let subview: UIView = { 
     let sv = UIView() 
     // some config, (i.e.: bgColor etc., frame is not yet _real_ 
     // can't yet access instance's frame and other properties 
     return sv 
    }() 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     commonInit() 
    } 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     commonInit() 
    } 

    func commonInit() { 
     // frame might be valid, preferably use layout constraints 
     addSubview(subview) 
    } 
} 

希望这有助于

+1

即使在调用super.init()之前,子视图也会被初始化,这并不是真正的“懒惰”:) - 也许你的意思是'懒惰的子视图:UIView = ...'(但它不再是常量) 。 –

+0

@MartinR你是对的,我对_lazy_的使用是误导性的。我的意思是_lazy commonInit()_: - /编辑它,希望它更清楚我的意图。谢谢 –

3

另一种选择:

class Test : UIView { 

    let subview:UIView 

    init(frame: CGRect?, coder: NSCoder?) { 

     // The first phase initialization here 
     subview = UIView() 

     if let frame = frame { 
      super.init(frame: frame) 
     } 
     else if let coder = coder { 
      super.init(coder: coder) 
     } 
     else { 
      super.init() 
     } 

     // the Second phase initialization here 
     self.addSubview(subview) 
    } 

    convenience init() { 
     self.init(frame: nil, coder: nil) 
    } 

    override convenience init(frame: CGRect) { 
     self.init(frame: frame, coder: nil) 
    } 

    required convenience init(coder aDecoder: NSCoder) { 
     self.init(frame: nil, coder: aDecoder) 
    } 
} 

一点点清洁的替代:

class Test : UIView { 

    let subview:UIView 

    private enum SuperInitArg { 
     case Frame(CGRect), Coder(NSCoder), None 
    } 

    private init(_ arg: SuperInitArg) { 

     subview = UIView() 

     switch arg { 
     case .Frame(let frame): super.init(frame:frame) 
     case .Coder(let coder): super.init(coder:coder) 
     case .None: super.init() 
     } 

     addSubview(subview) 
    } 

    convenience init() { 
     self.init(.None) 
    } 
    override convenience init(frame: CGRect) { 
     self.init(.Frame(frame)) 
    } 
    required convenience init(coder aDecoder: NSCoder) { 
     self.init(.Coder(aDecoder)) 
    } 
} 
+0

我仍然在努力是否关闭问题作为http://stackoverflow.com/questions/24023412/how-to-implement-two-inits-with-same-content-without-code-duplication-in -swift,它有一个类似的答案http://stackoverflow.com/a/26772103/1187415(即使你的看起来更清洁)。 –

+0

哇,我没有看到答案。至少,我的回答绝对是一个骗局。 – rintaro

1

这非常适用于米E:

// Declare this somewhere (it can be used by multiple classes) 
class FrameCoder: NSCoder { 
    let frame: CGRect 
    init(_ frame: CGRect) { 
     self.frame = frame 
     super.init() 
    } 
} 

然后,当你想有一个共同的初始化模式,使用此:

class MyView: UIView { 

    let something: SomeType 

    required init(coder aDecoder: NSCoder) { 
     if (aDecoder is FrameCoder) { 
      super.init(frame: (aDecoder as! FrameCoder).frame) 
     } 
     else { 
      super.init(coder: aDecoder) 
     } 

     // Common initializer code goes here... 
     something = // some value 
    } 

    override init(frame: CGRect) { 
     self.init(coder: FrameCoder(frame)) 
    } 
} 

使用这种方法,你不必为let定义创建默认值的优势 - 您可以在上下文中将它们设置为正确的值,就像只有一个初始化程序一样。

请注意,您可以使用此技术获取任意值的初始值设定项(不仅适用于init(frame: CGRect)) - 您可以创建特定的NSCoder子类来包装任何需要传递给初始值设定项的值和类型,然后将其链接到您的init(coder:)方法。

(也有可能是某种方式与通用做到这一点...还没有完全想通了没有!任何人......?)