在 Objective-C
中,我发展了同时拥有 awakeFromNib
和 initWithFrame:
方法的模式,后者调用了它们的 super
的,然后调用一个 _commonInit
,我把我自己的所有代码都放在那里。例如
- (void)_commonInit {
// Initialize stuff here
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self _commonInit];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self _commonInit];
}
所以我试图在我移植到 Swift 的 UIView 子类中重用这个模式:
func _commonInit() {
// initialize code here
}
override init(frame:CGRect) {
super.init(frame:frame)
self._commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self._commonInit()
}
这是正确的做法吗?我很好奇为什么 init(coder...)
是必需的
。特别是当我所做的只是调用 super
版本时。我似乎记得我在 Objc
版本中使用 awakeFromNib
的原因是因为从 nib 恢复应用的任何更改直到晚于 initFromCoder:
.
最佳答案
拥有一个从你的初始化器调用的 commonInit
方法对于你有多个指定(或要求)初始化器的情况来说是一个完美的模式。不过,这不是唯一的模式。
依次解决你的每一个疑惑(并补充一些相关的要点)...
init(coder:)
是必需的,因为您正在子类化声明符合NSCoding
协议(protocol)的类。该协议(protocol)要求所有子类的所有实例都能够从存档中初始化。不过,您实际上不必在
init(coder:)
中执行任何操作,除非您将状态保存在encodeWithCoder(_:)
中。但是因为子类可能具有未继承的可编码状态,所以初始化安全要求子类负责此初始化程序(即使它所做的只是调用 super)。您在从 nib/ Storyboard加载的任何自定义类中使用
awakeFromNib()
来处理仅在连接对象的导出和操作后才需要发生的初始化。当 Cocoa (Touch) 加载一个 nib 时,它首先初始化每个对象(使用
init(coder:)
),然后在所有对象“事件”之后,它连接所有IBOutlet
变量和控件发送的所有IBAction
的目标.完成所有这些后,它调用awakeFromNib()
。在 Swift 中使用
commonInit
模式有一个警告——甚至是一个悖论:您必须在调用super.init()
之前初始化属性(又名实例变量),并且您不能访问self
(包括调用self
上的方法) ) 直到调用super.init()
之后。因此,您不能使用commonInit
方法来设置属性的初始值。您可以具有类型为隐式展开的可选类型的属性。这些会自动初始化为
nil
,然后您可以在commonInit
方法中设置一个“真实的”初始值:var name: String! init(frame: CGRect) { super.init(frame: frame) self.commonInit() } init(coder: NSCoder) { super.init(coder: coder) self.commonInit() } private func commonInit() { name = // something... }
解决此问题的另一种模式是为每个属性提供初始值设定项(甚至惰性初始值设定项)。如果这样做,则不需要 commonInit
方法,因为将从实际使用的 init
中隐式调用属性初始值设定项(或者在惰性初始值设定项的情况下,当首次访问属性时)。
class MyView: UIView {
let fileManager = NSFileManager.defaultManager()
let appDelegate = UIApplication.sharedApplication().delegate as! MyAppDelegate
lazy var name: String = { /* compute here */ }()
init(frame: CGRect) { super.init(frame: frame) }
init(coder: NSCoder) { super.init(coder: coder) }
// ...
}
最后,如果你确实提供了一个 commonInit
(或类似的)方法,你不需要用初始下划线或任何东西来破坏名称——Swift 有内置的访问控制,所以任何您不想向类外的调用者公开的方法可以简单地标记为 private
。
关于swift - 在 Swift 中初始化 UIViews 的惯用模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29956195/