通过协议更新UILabel导致崩溃(找到零)

时间:2016-10-05 09:23:44

标签: ios swift

我想为新应用实现MVP模式。因此除了专门涉及UI元素的视图之外,View不应该具有任何逻辑。因此,我想要从"口译员" (解释后面代码中的用户输入),然后从我的模型中请求数据并将其提供给" Presenter"。演示者拥有一个包含视图功能的协议。

问题是:从演示者调用updateUIData()会产生

  

致命错误:在解包可选值时意外发现nil

从同一位置的View中调用函数工作正常。

我怀疑错误来自演示者初始化中特定MainViewController的初始化,但如果我的猜测正确,我不知道如何解决这个问题。

这是我的(相关)代码:

MainViewController:

class MainViewController: UIViewController {

    lazy var interpreter = Interpreter() // lazy needed b/c Interpreter holds Presenter which holds MainViewController

    @IBOutlet var dateLabel: UILabel!
    @IBOutlet var totalTimeLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        // updateUIData()
        requestData()
    }

    func requestData() {
        interpreter.requestData()
    }

}

extension MainViewController: MainViewSetters {

    func updateUIData() {
        dateLabel.text = "Data"
        totalTimeLabel.text = "loaded"
    }

}

MainViewSetters(协议):

protocol MainViewSetters {
    func updateUIData()
}

解释器:

class Interpreter {

    let presenter = Presenter()

    func requestData() {
        // normally: get data from model and pass it to presenter
        presenter.presentData()
    }

}

主讲人:

class Presenter {

    let mainView: MainViewSetters

    init(withMainViewController mainVC: MainViewSetters = MainViewController()) {
        mainView = mainVC
    }

    func presentData() {
        mainView.updateUIData()   
    }

}

2 个答案:

答案 0 :(得分:1)

您的问题是,您没有将MainViewController的引用传递给Presenter的实例。

此代码:

lazy var interpreter = Interpreter()

应该更像这样:(这里需要输入类型因为懒惰,编译器无法正确推断)

lazy var interpreter: Interpreter = Interpreter(for: self)

然后你必须在Interpreter中创建一个特殊的初始化器,它将viewController实例传递给它的presenter属性:

class Interpreter {

    let presenter: Presenter

    init(for viewController: MainViewSetters) {
        presenter = Presenter(withMainViewController: viewController)
    }

    func requestData() {
        // normally: get data from model and pass it to presenter
        presenter.presentData()
    }

}

我还强烈建议您将默认值移至Presenter的{​​{1}}方法,您不太可能希望将init的随机实例指定为{{1}任何MainViewController对象。

最后,请注意,此代码正在创建保留周期,您的mainView实例和Presenter实例都不会被释放。这是因为MainViewController类拥有对Presenter实例及其属性Presenter的强引用。要解决此问题,您必须将MainViewController标记为,并将其设为可选

请参阅下面的固定实施:

mainView

要使mainViewclass Presenter { weak var mainView: MainViewSetters? init(withMainViewController mainVC: MainViewSetters) { mainView = mainVC } func presentData() { mainView?.updateUIData() } } 类型的属性上可接受(不是真实类型而只是协议),您必须指定其仅应用于类的协议:

weak

答案 1 :(得分:1)

您正在初始化传递默认MainViewController()的解释器。 从以下代码更改该代码:

lazy var interpreter = Interpreter()

lazy var interpreter = Interpreter(withMainViewController: self)