【问题标题】:Need technical advice about passing data through UINavigationController需要有关通过 UINavigationController 传递数据的技术建议
【发布时间】:2020-03-29 02:53:32
【问题描述】:

首先是一个简短的介绍,我对 Swift 和一般编程都比较陌生,去年一直在做这件事,并且热爱这个广阔世界的每一个新事物。

我的帖子是关于一些技术建议以及了解我公司正在做出的决定是否有意义。我将首先讨论所建议的设计,然后是我的结论。

正在实施的设计;

我们正在开发一个大型应用程序,这个应用程序有一些流程,它们遵循 5 到 8 个控制器的序列,我们的团队负责人坚持这种设计模式; 让我们称第一个控制器为持有者,持有者(黑色边框)负责拥有一个容器,这个容器有一个适当的导航控制器(红色边框),而且持有者持有那些辅助控制器正在生成的所有数据(橙色) .

此模式试图实现的图表

为此,每个红色边框控制器都有一个方法:

private func getParent() -> HolderViewController? {

    if let parent = navigationController?.parent as? HolderViewController {

        return parent
    }
    return nil
}

为了写在持有人中,我们调用方法;​​

   getParent().someModelInstance.someModelProperty = "some data”

结论;

通过导航控制器传递数据似乎违背了单一职责原则。 在每个控制器中创建强引用,即使我确保在流程结束时正确 deinit 导航控制器,这似乎也不是一个好的选择,这可能会导致内存泄漏和保留周期。 由于某种原因,我无法确保两个控制器尝试同时访问相同的属性。 这似乎是单例设计模式,但“范围”有限

决议;

由于我是新人,我在一家公司工作,而且每个公司都有等级制度,所以我的首要目标是了解我的结论是否错误,并对此有更好、更简洁的解释。

首先,为了解决内存泄漏的问题,我创建了一个并发队列。 我没有直接访问模型来写入它,而是尝试通过一种方法来解决它,该方法将使用 keyPath 而不是直接使用模型,这是我用来在模型中写入的方法;

在持有人:

class HolderViewController: UIViewController {

   @IBOutlet weak var bottomNavigationContainer: UIView!

   private var bottomNavigationController: UINavigationController!
   private var someModel: SomeModel!
   private let concurrentQueue: DispatchQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)

    override func viewDidLoad() {
        super.viewDidLoad()

        setupBottomNavigation()
    }

    private func setupBottomNavigation() {

        rootController = SecondayViewController()

        if let root = rootController {

            bottomNavigationController = UINavigationController(rootViewController: root)
            addChild(bottomNavigationController)
            bottomNavigationController.view.frame = bottomNavigationContainer.bounds
            bottomNavigationContainer.addSubview(bottomNavigationController.view)
        }
    }
}

extension HolderViewController {

    public func setValueInModel<Value>(_ value: Value, forKey path: WritableKeyPath<SomeModel, Value>) {

       concurrentQueue.async(flags: .barrier) { [weak someModelInstance] in

            if var obj = someModelInstance {

                obj[keyPath: path] = value
            }
        }
    }

    public func readFromHolder() -> SomeModel {

        concurrentQueue.sync {
            return self.someModelInstance
        }
    }
}

这个方法会这样调用;

class SecondayViewController: UIViewController {

    var someString: String = "some data"

    private func getParent() -> HolderViewController? {

      if let parent = navigationController?.parent as? HolderViewController {

        return parent
      }
      return nil
    }

    private func setValueInHolder(string: String) {

      getParent().setValueInModel(string, forKey: \.someModelProperty)

    }

    private func readFromHolder() -> String {

       return getParent().readFromHolder().someModelProperty
    }

} 

这看起来像是一些杂乱的代码来做一件简单的事情,我们可以使用闭包、委托、segues 等……但我的团队领导不喜欢其他更简单和更常见的解决方案。忘了说,我们的每个控制器都有一个 xib,我们不使用故事板。我知道如何使用其他选项的基础知识,我正在尝试了解这是或不是一个好的解决方案,以及为什么,以及我的思维方式和方法是否有意义。

请记住,我得出的每个结论或实施的每个解决方案都可能是错误的,这就是为什么我要与您分享我的想法,以便从您的建议和经验中学习

提前致谢。 :) 保持安全!

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    作为前言:这类问题可能更适合code review community

    我可以通过查看第一个图表来判断您的担忧是从哪里开始的。将数据流视为图表,您会注意到存在一个循环。有时在某个地方,它可能会被完全使用(性能比什么都重要),在这种情况下,记住内存管理非常重要。

    您可能会注意到,在调用 addChild(_:) 之后,会将包含的控制器添加到 children: [UIViewController] 并设置其 parent 属性。但这是图书馆为您完成的。

    类似于ViewController 的概念,其中视图是哑的,逻辑包含在视图控制器中。同样,我会将子视图控制器与父视图控制器分离,将大部分逻辑从子视图控制器中移开,并使用适当的机制进行耦合。

    话虽如此,我很少发现将 KVO 与 swift 一起使用有多大价值,还有其他机制可以完成同样的事情。

    这确实取决于控制器之间的关系是什么以及它们具有什么功能。没有什么可以摆脱的。我会留给你去发现你真正需要什么解决方案,我找到的最好的指导是在NSHipster's blog上描述用于松散/强耦合与一对一和一对一的通信机制很多关系。

    还应该指出:

    if let root = rootController {
        bottomNavigationController = 
            UINavigationController(rootViewController: root)
        addChild(bottomNavigationController)
        bottomNavigationController.view.frame = 
            bottomNavigationContainer.bounds
        bottomNavigationContainer.addSubview(bottomNavigationController.view)
    }
    

    您应该使用willMovedidMove 并将任何设置移到那里。

    【讨论】:

    • 非常感谢您的回复!我不知道海湾评论社区,我将把我的问题移到那里!
    • 代码审查和堆栈溢出之间的区别 tl;博士:堆栈溢出 = 损坏的代码通常包括编译器错误和/或堆栈跟踪。代码审查 = 工作代码,在结构、格式、样式等方面寻找建议或改进。
    猜你喜欢
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    • 1970-01-01
    • 2011-05-13
    相关资源
    最近更新 更多