为什么让一个视图控制器自我解除不是一个好的做法?

17

我有两个视图控制器,MainVCModalVC

当用户点击 MainVC 上的按钮时,模态视图控制器会出现。

然后,用户可以点击另一个按钮来关闭它并返回到主视图。

我尝试过这两种方法,它们都能实现同样的功能:关闭模态视图控制器:

//method 1:
//  File: ModalVC.swift
//
@IBAction func dismissTapped() {
     self.dismissViewControllerAnimated(false, completion: nil);
}

就像我之前所说,那个方法也可以正常运行,但考虑另一种方法:使用委托让主控制器来执行dismiss操作:

// method 2: part A 
// File: ModalVC.swift
// 
protocol ModalVCDelegate {
    func modalVCDismissTapped();
}
...
...
...
var delegat:ModalVCDelegate? = nil;
...
...
@IBAction func dismissTapped() {
    delegate.modalVCDismissTapped();
}

在主视图控制器的自定义类文件上:

// method 2: part B
// File: MainVC.swift

class MainVC : UIViewController, ModalVCDelegate {
...
...
    func modalVCDismissTapped() {
        self.dismissViewControllerAnimated(false, completion: nil);
    }
}

这两种方法已经做了必要的工作,那我是否需要担心可能出现的内存泄漏问题呢?

任何解释都会有所帮助。

4个回答

20

使用委托是解散视图控制器的最佳更灵活的方式。
其目的是,在未来的某个时间或代码中的其他地方,您可能会重用此VC,但由于某些原因,您可能不会将其以模态方式呈现,而是将其推入导航堆栈。 因此,您的ModalVC不知道它是如何被呈现的,但委托知道。
在这种情况下,您可以在代码中有2个位置

  1. 您将其以模态方式呈现,并且委托调用

    [self dismiss...]
    
  2. 你将其推入导航栈并委托调用

  3. [self.navigationController popView...]
    
  4. 您将其作为子VC添加并委托调用

  5. [someParentVC removeChild..] 
    

    或任何其他适当的工作流程以将其删除。


很棒的回答。总结成一句话:你的“模态”视图控制器不知道客户将如何使用/呈现它,因此它也必须负责将其解除。 - Christian Schnorr
1
简而言之:创建并拥有它的人也应该处理它的解雇。 - Eiko

0

当前呈现的视图控制器不知道自己正在被呈现,因此不应该知道如何解除自身。

Apple 建议从呈现视图控制器中解除一个已经呈现的视图控制器 https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss

为避免在您的情况下出现泄漏,请始终将委托变量声明为 weak。为此,您的协议应继承自 AnyObject

protocol ModalVCDelegate: AnyObject {
   func modalVCDismissTapped()
}

weak var delegate: ModalVCDelegate?

另一种方法是在呈现的VC上创建一个闭包变量,并在初始化后将dismiss操作传递给呈现的VC,然后在呈现的VC上执行该闭包以响应某个操作。

呈现VC设置以进行呈现

class PresentingViewController: UIViewController {
  @IBAction func buttonTapped(_ sender: Any) {
     let presentedVC = PresentedViewController()
     presentedVC.modalPresentationStyle = .fullScreen
     presentedVC.onDismissAction = { [weak self] in
       self?.dismiss(animated: true)
     }
     self.present(presentedVC, animated: true, completion: nil)
  }
}

为dismiss设置了VC呈现

class PresentedViewController: UIViewController {
  var onDismissAction: (() -> Void)?

  @IBAction func exitButtonTapped(_ sender: Any) {
    onDismissAction?()
  }
}

0

dismiss(animated: Bool) 会首先关闭当前呈现的视图控制器。如果没有呈现任何视图控制器,则会关闭自身。

如果要关闭自身,您需要使用:

presentingViewController.dismiss(animated: true)


0

Modo Ltunzher的答案很好。 我个人更喜欢将闭包传递给“子”对象,当“子”完成时会回调“父”对象,并且作为额外奖励,我还可以传回值/结果。

例如:我展示一个QRCode,当QRCode被识别时,它将关闭并回调。

extension UIViewController {


    func presentQRCode( pushed: Bool, callback: @escaping QRCodeCallBack){

        let qrVC = ScanQRCodeController(nibName: "ScanQRCodeController", bundle: nil)
        qrVC.callback = callback

        if pushed{
            let nc = self.navigationController!
            nc.pushViewController(qrVC, animated: true)

        }else{
            self.present(qrVC, animated: true, completion: nil)
        }

    }


    func dismissQRCode(pushed: Bool){

        if pushed{
            let nc = self.navigationController!
            nc.popViewController(animated: true)
        }else{
            self.dismiss(animated: true, completion: nil)
        }
    }
}

in "father"

   @IBAction func doScanCodeAction(_ sender: UIBarButtonItem) {
        let pushed = true
        self.presentQRCode(pushed: pushed, callback: { (string: String?) in

            if let qrCode = string{
                self.fillFieldsWith(qrCode: qrCode)
            }else{
                #if DEBUG
                print("QR code error")
                #endif
            }

            self.dismissQRCode(pushed: pushed)
        }
        )
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接