【问题标题】:Swift View and Controller shared variable initialization troublesSwift View 和 Controller 共享变量初始化麻烦
【发布时间】:2015-12-15 00:53:44
【问题描述】:

我正在将一些代码从 Objc 移植到 Swift。并在 Swift 的初始化生命周期中苦苦挣扎。从简化的角度来看,这里有 4 名球员我想发挥作用:

  1. 名为Program 的对象。这是我此时的主要顶级模型对象。其余 3 名玩家都想访问他的一个实例。
  2. 一个ProgramEditController,画在我的主Main.storyboard中。他负责实例化一个初始 Program,这不能作为属性初始化器直接完成。
  3. 顶级自定义 UIView 子类,称为 ProgramTimelineView。绘制在 Main.storyboard 中,管理各种专门的子视图。链接到我的 ViewController 的属性。也有它的子视图的属性。它想要访问程序,因此它可以进行布局并将其传递给子视图。
  4. ProgramTimelineView 的特定子视图称为ProgramGridView。这些不是在 XCode 画布工具中绘制的,而是直接由包含的 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
    }
}

我认为会发生的事情:

  1. ProgramEditController.init(nibName...) 会先触发。
  2. 超级调用会导致我的ProgramTimelineView.init(coder) 触发。
  3. ProgramTimelineView 实例将首先调用 gridView 初始化程序,将其设置为新的 ProgramGridView 视图
  4. ProgramTimelineView.init(coder) 的其余部分将运行,这会将 gridView 添加到视图树中。
  5. 控制将返回到ProgramEditController.init(nibName) 初始化程序。控制器的 program 属性将被填充。
  6. 绑定的timelineView 将设置其program 属性。
  7. ProgramTimelineView 将依次设置gridViewprogram 属性。

不过,在第 4 步和第 5 步之间似乎发生了drawRect()。这会导致 seg 错误,因为尚未设置 gridView 的程序!但为什么此时要发出 drawRect() 呢?我认为在所有初始化程序都被触发之前不会发生这种情况。但很明显,一些副作用正在发生。避免这种情况的正确模式/习语是什么?我真的宁愿不要把所有的program! 变成program? 然后把 let/guards 放在每个地方。

【问题讨论】:

    标签: swift uiview uiviewcontroller initialization swift2


    【解决方案1】:

    原来在我原来的前提中有一个错误的假设(通常是这种情况)。

    UIViewController.init(nib...) 在从界面构建器资产创建时不会被调用。但是在接口构建器中链接的局部变量(隐式包装)也没有在init(coder) 处设置/实现。正确的方法需要两个调整:

    1. program var 的设置移动到init(coder) 初始化器:

    例如

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.program = self.site.newProgram()
    }
    
    1. 将其转发到 viewDidLoad() 覆盖中的视图。

    例如

    override func viewDidLoad() {
        super.viewDidLoad()
        self.timelineView.program = self.program
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-18
      • 2012-12-03
      • 2013-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多