Swift View和Controller共享变量初始化麻烦
Swift View and Controller shared variable initialization troubles
我正在将一些代码从 Objc 移植到 Swift。并与 Swift 的初始化生命周期作斗争。从简化的角度来看,我在这里尝试让 4 位玩家发挥作用:
- 一个名为
Program
的对象。这是我目前主要的顶级模型对象。剩下的3个玩家都想获得他的实例
- 一个
ProgramEditController
,画在我的主Main.storyboard。他负责实例化一个初始程序,这不能作为 属性 初始化器直接完成。
- 一个顶级自定义 UIView 子类,称为
ProgramTimelineView
。 Painted in Main.storyboard,管理各种专门的子视图。链接到我的 ViewController 的 属性。也有它的子视图的属性。它想要访问程序,所以它可以进行布局并将其传递给子视图。
ProgramTimelineView
的一个特定子视图称为 ProgramGridView
。这些不是在 XCode canvas 工具中绘制的,而是通过包含 ProgramTimelineView
直接实例化的。它想要访问该程序。用它来做他的习惯 drawRect
.
这是我的控制器的相关代码:
class ProgramEditController: UIViewController {
// MARK: - Variables
@IBOutlet var timelineView:ProgramTimelinesView!
var site = Site()
var program:Program!
// MARK: - Initialize
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// set up the new program
self.program = self.site.newProgram()
// get it into our top view before it starts drawing
self.timelineView.program = self.program
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) // Why does Swift make me have a redundant thing here?
}
}
对于 ProgramTimelinesView:
class ProgramTimelinesView: UIView {
// MARK: - Variables
var gridView = ProgramGridView()
var program:Program! {
didSet {
self.gridView.program = self.program
}
}
// MARK: - Initialization
func addGridView() {
self.gridView.alpha = 0.0
self.gridView.opaque = false
self.addSubview(self.gridView)
}
func commonInit() {
self.addGridView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
}
最后 ProgramGridView
:
class ProgramGridView: TimeAxisView {
// MARK: - Variables
var program:Program!
override func drawRect(rect: CGRect) {
// access self.program in here
}
}
我认为会发生什么:
ProgramEditController.init(nibName...)
会先触发。
- super 调用会导致我的
ProgramTimelineView.init(coder)
触发。
- ProgramTimelineView 实例将首先调用
gridView
初始化程序,将其设置为新的 ProgramGridView
视图
ProgramTimelineView.init(coder)
的其余部分将 运行,这会将 gridView 添加到视图树中。
- Control 会 return 到
ProgramEditController.init(nibName)
初始值设定项。将填充控制器的 program
属性。
- 绑定
timelineView
将设置 program
属性。
ProgramTimelineView
将依次设置 gridView
. 的 program
属性
不过,在第 4 步和第 5 步之间似乎发生了 drawRect()
。这会导致段错误,因为尚未设置 gridView 的程序!但为什么它在此时发出 drawRect()?我认为在所有初始化器都被触发之前不会发生这种情况。但很明显,一些副作用正在发生。避免这种情况的正确 pattern/idiom 是什么?我真的不想把所有的 program!
都变成 program?
然后把 let/guards 放在每个地方。
事实证明我原来的假设是错误的(通常是这样)。
UIViewController.init(nib...)
在从界面生成器资产创建时不会被调用。但是在接口构建器中链接的局部变量(隐式包装)在 init(coder)
点也不是 set/realized 。正确的方法需要两个调整:
- 将
program
var 的设置移动到 init(coder)
初始值设定项:
例如
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.program = self.site.newProgram()
}
- 将其转发到
viewDidLoad()
覆盖中的视图。
例如
override func viewDidLoad() {
super.viewDidLoad()
self.timelineView.program = self.program
}
我正在将一些代码从 Objc 移植到 Swift。并与 Swift 的初始化生命周期作斗争。从简化的角度来看,我在这里尝试让 4 位玩家发挥作用:
- 一个名为
Program
的对象。这是我目前主要的顶级模型对象。剩下的3个玩家都想获得他的实例 - 一个
ProgramEditController
,画在我的主Main.storyboard。他负责实例化一个初始程序,这不能作为 属性 初始化器直接完成。 - 一个顶级自定义 UIView 子类,称为
ProgramTimelineView
。 Painted in Main.storyboard,管理各种专门的子视图。链接到我的 ViewController 的 属性。也有它的子视图的属性。它想要访问程序,所以它可以进行布局并将其传递给子视图。 ProgramTimelineView
的一个特定子视图称为ProgramGridView
。这些不是在 XCode canvas 工具中绘制的,而是通过包含ProgramTimelineView
直接实例化的。它想要访问该程序。用它来做他的习惯drawRect
.
这是我的控制器的相关代码:
class ProgramEditController: UIViewController {
// MARK: - Variables
@IBOutlet var timelineView:ProgramTimelinesView!
var site = Site()
var program:Program!
// MARK: - Initialize
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// set up the new program
self.program = self.site.newProgram()
// get it into our top view before it starts drawing
self.timelineView.program = self.program
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) // Why does Swift make me have a redundant thing here?
}
}
对于 ProgramTimelinesView:
class ProgramTimelinesView: UIView {
// MARK: - Variables
var gridView = ProgramGridView()
var program:Program! {
didSet {
self.gridView.program = self.program
}
}
// MARK: - Initialization
func addGridView() {
self.gridView.alpha = 0.0
self.gridView.opaque = false
self.addSubview(self.gridView)
}
func commonInit() {
self.addGridView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
}
最后 ProgramGridView
:
class ProgramGridView: TimeAxisView {
// MARK: - Variables
var program:Program!
override func drawRect(rect: CGRect) {
// access self.program in here
}
}
我认为会发生什么:
ProgramEditController.init(nibName...)
会先触发。- super 调用会导致我的
ProgramTimelineView.init(coder)
触发。 - ProgramTimelineView 实例将首先调用
gridView
初始化程序,将其设置为新的ProgramGridView
视图 ProgramTimelineView.init(coder)
的其余部分将 运行,这会将 gridView 添加到视图树中。- Control 会 return 到
ProgramEditController.init(nibName)
初始值设定项。将填充控制器的program
属性。 - 绑定
timelineView
将设置program
属性。 ProgramTimelineView
将依次设置 gridView
. 的
program
属性
不过,在第 4 步和第 5 步之间似乎发生了 drawRect()
。这会导致段错误,因为尚未设置 gridView 的程序!但为什么它在此时发出 drawRect()?我认为在所有初始化器都被触发之前不会发生这种情况。但很明显,一些副作用正在发生。避免这种情况的正确 pattern/idiom 是什么?我真的不想把所有的 program!
都变成 program?
然后把 let/guards 放在每个地方。
事实证明我原来的假设是错误的(通常是这样)。
UIViewController.init(nib...)
在从界面生成器资产创建时不会被调用。但是在接口构建器中链接的局部变量(隐式包装)在 init(coder)
点也不是 set/realized 。正确的方法需要两个调整:
- 将
program
var 的设置移动到init(coder)
初始值设定项:
例如
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.program = self.site.newProgram()
}
- 将其转发到
viewDidLoad()
覆盖中的视图。
例如
override func viewDidLoad() {
super.viewDidLoad()
self.timelineView.program = self.program
}