如果你没接触过 flutter的 Get 框架或者 java中springMVC 的DI设计思想,那你可以稍微看看我下面的介绍.
本篇主要为解决MVP 模式中全局 P 查找问题,我们将 Controller 的职责交给P,并且将 P 控制反转给我们的 Get管理器,解决的痛点
1.剥离掉 ViewController 的Controller职责
2.使 ViewController 下所有的子 View 都可以不通过传值方式查找到 P管理者.
Get 管理器
使用NSMapTable存储我们的业务 Controller,相当于弱引用了 GetController,不印象 Controller 的正常释放.
class Get {
static let to = Get()
private init() {}
private var controllerMap = NSMapTable<NSString, GetController>.strongToWeakObjects()
let (input, output) = Signal<String, Never>.pipe()
static func find<T: GetController>(_ type: T.Type, identify: String? = nil) -> T?{
let key = (type.className + (identify ?? "")).nsString
return to.controllerMap.object(forKey: key) as? T
}
static func put(_ c: GetController, identify: String? = nil, operation: GetOperation = .ignore) {
let key = (c.className + (identify ?? "")).nsString
if let _ = to.controllerMap.object(forKey: key) {
switch operation {
case .ignore:
return
case .replace:
to.controllerMap.setObject(c, forKey: key)
}
} else {
to.controllerMap.setObject(c, forKey: key)
}
}
}
GetController 基类
我用 controller持有了 page(UIVIewContorller),将 page(UIViewController )所有的职责反转给 controller,如果你只是 iOS 开发,这个类你可以理解为 viewmodel,或者MVP 中的Presenter
open class GetController: NSObject {
/// page页
weak var page: UIViewController?
/// 生命周期信号
public var life: Signal<GetLifeCycle, Never> {
lifeCycleSignal.output
}
/// 生命周期信号
private let lifeCycleSignal = Signal<GetLifeCycle, Never>.pipe()
init(_ page: UIViewController? = nil) {
super.init()
self.page = page
// let viewWillAppear = #selector(UIViewController.viewWillAppear(_:))
page?.r.trigger(for: #selector(UIViewController.viewDidLoad)).observeValues {[weak self] _ in
self?.lifeCycleSignal.input.send(value: .viewDidLoad)
self?.onViewDidLoad()
}
page?.r.trigger(for: #selector(UIViewController.viewWillAppear(_:))).observeValues {[weak self] _ in
self?.lifeCycleSignal.input.send(value: .viewWillAppear)
self?.onViewWillAppear()
}
page?.r.trigger(for: #selector(UIViewController.viewDidAppear(_:))).observeValues {[weak self] _ in
self?.lifeCycleSignal.input.send(value: .viewDidAppear)
self?.onViewDidAppear()
}
page?.r.trigger(for: #selector(UIViewController.viewWillDisappear(_:))).observeValues {[weak self] _ in
self?.lifeCycleSignal.input.send(value: .viewWillDisappear)
self?.onViewWillDisappear()
}
page?.r.trigger(for: #selector(UIViewController.viewDidDisappear(_:))).observeValues {[weak self] _ in
self?.lifeCycleSignal.input.send(value: .viewDidDisappear)
self?.onViewDidDisappear()
}
onInit()
}
open func onInit() {
log("~~~\(Self.className)__\(#function)}")
}
open func onViewDidLoad() {
log("~~~\(Self.className)__\(#function)}")
}
open func onViewWillAppear() {
log("~~~\(Self.className)__\(#function)}")
}
open func onViewDidAppear() {
log("~~~\(Self.className)__\(#function)}")
}
open func onViewWillDisappear() {
log("~~~\(Self.className)__\(#function)}")
}
open func onViewDidDisappear() {
log("~~~\(Self.className)__\(#function)}")
}
open func onDispose() {
log("~~~\(Self.className)__\(#function)}")
}
deinit {
onDispose()
}
}
Get 枚举,Model 等
public enum GetOperation: Int {
case ignore
case replace
}
public enum GetLifeCycle: Int {
case viewDidLoad
case viewWillAppear
case viewDidAppear
case viewWillDisappear
case viewDidDisappear
}
Get 协议
///// 控制器要遵守的协议
//protocol GetControllerProvider {
// associatedtype T: GetController
// var controller: T? { get }
//}
/// controller 要遵守的协议
protocol GetControllerProtocol {
static var find: Self? { get }
static func find(_ identify: String?) -> Self?
}
extension GetControllerProtocol where Self: GetController {
static var find: Self? {
Get.find(Self.self)
}
static func find(_ identify: String? = nil) -> Self? {
Get.find(Self.self, identify: identify)
}
}
测试用例
class TestGetPage: UIViewController {
lazy var controller: TestGetController = TestGetController(self)
init() {
super.init(nibName: nil, bundle: nil)
Get.put(controller)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let view1 = TestGetView1.shared
let view2 = TestGetView2()
override func viewDidLoad() {
super.viewDidLoad()
view1.add(to: self.view)
view2.add(to: self.view)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view1.frame = MakeRect(0, 0, view.width, controller.viewHeight1)
view2.frame = MakeRect(0, view1.frame.maxY, view.width, controller.viewHeight2)
}
deinit {
log("~~~\(Self.className)释放了")
}
}
GetController子类 TestController
final class TestGetController: GetController, GetControllerProtocol {
static var find: TestGetController? { Get.find(TestGetController.self) }
var viewHeight1:CGFloat = 100;
var viewHeight2:CGFloat = 100;
func changeHeight() {
viewHeight1 = viewHeight1 == 150 ? 100 : 150
page?.viewDidLayoutSubviews()
}
deinit {
log("~~~\(Self.className)释放了")
}
}
子视图查找 getcontorller
class TestGetView1: UIView {
static let shared = TestGetView1()
init() {
super.init(frame: .zero)
self.backgroundColor = .red
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit() {
self.r.tap.observeValues {[weak self] _ in
guard let self = self else { return }
TestGetController.find?.changeHeight()
}
}
deinit {
log("~~~TestGetView1释放了")
}
}
注意,GetController 在业务中不能被不可释放类型强引用(比如单例),所以推荐我所写的 Protocol 去查找这个 controller,另外,如果该页面可被重复 push,那么,需要让子视图绑定一个 identify,比如 userId 等等,用于区分查找.
PS: 本篇写的急,因为工作业务比较急迫,后续补充设计思路吧,其实框架十分简单,大家认真看看即可,欢迎提意见