Swift实现iOS通讯录风格Segue的相关问题
我来逐个拆解你遇到的这三个问题,帮你理清Segue和视图控制器层级的那些事儿:
问题1:为何必须强制向下转为UINavigationController,而非预期的PeopleViewController?
这是因为视图控制器的层级关系导致的:你把PeopleViewController嵌入在了UINavigationController里,当你以模态方式呈现NewPersonViewController时,presentingViewController指向的是那个负责展示PeopleViewController的导航控制器,而不是PeopleViewController本身。
简单说,当前的层级链是:UINavigationController → 子控制器PeopleViewController → 模态呈现NewPersonViewController。所以NewPersonViewController的presentingViewController是导航控制器,你直接转成PeopleViewController肯定会失败,必须先拿到导航控制器,再从它的子控制器列表里找到PeopleViewController。
另外补充一句:用childViewControllers.first找PeopleViewController有点不太靠谱,如果导航栈里有其他控制器,可能会出错,更稳妥的方式是用navigationController?.topViewController来获取当前导航栈最顶部的PeopleViewController。
问题2:闭包中仅对pvc使用weak,未处理cvc是否会导致内存泄漏?
不会有内存泄漏的风险,原因如下:
你已经把pvc声明为weak var,这意味着闭包捕获的是UINavigationController的弱引用。而cvc是通过pvc?.childViewControllers.first获取的临时变量,它的引用依赖于弱引用的pvc——闭包并不会直接强持有cvc,只是在闭包执行时,通过弱引用的pvc去访问它的子控制器。
不过这里有个小问题:如果在dismiss的过程中,导航控制器被释放了,pvc会变成nil,那cvc也会是nil,这时候后续的代码就不会执行。这其实是合理的,因为如果原导航控制器都不存在了,也就没必要再执行Segue了。
如果要更严谨的话,可以把代码调整成这样,确保在安全的环境下访问:
self.dismiss(animated: false) { [weak self, weak pvc] in guard let self = self, let pvc = pvc, let cvc = pvc.childViewControllers.first as? PeopleViewController else { return } cvc.selectedPerson = self.person cvc.performSegue(withIdentifier: .showPerson, sender: nil) }
这样明确捕获weak self和weak pvc,避免任何潜在的引用问题。
问题3:是否有更优实现方式?
你当前的实现直接依赖视图控制器的层级关系,强转类型的方式耦合性很高,一旦后续视图层级变化,很容易崩溃。推荐两种更优雅的实现方式:
方式1:代理模式(经典解耦方案)
首先定义一个代理协议:
protocol NewPersonViewControllerDelegate: AnyObject { func newPersonViewController(_ vc: NewPersonViewController, didAddPerson person: Person) }
然后在NewPersonViewController里添加代理属性:
class NewPersonViewController: UIViewController { weak var delegate: NewPersonViewControllerDelegate? var person: Person? // ... 其他代码 }
修改addNewPerson方法:
extension NewPersonViewController { func addNewPerson() { guard let person = person else { return } delegate?.newPersonViewController(self, didAddPerson: person) dismiss(animated: true) } }
最后让PeopleViewController实现代理,并在present时设置代理:
extension PeopleViewController: NewPersonViewControllerDelegate { func newPersonViewController(_ vc: NewPersonViewController, didAddPerson person: Person) { self.selectedPerson = person performSegue(withIdentifier: .showPerson, sender: nil) } // 假设你是通过按钮触发present的 func presentNewPersonVC() { let newVC = NewPersonViewController() newVC.delegate = self present(newVC, animated: true) } }
方式2:闭包回调(更简洁的现代方案)
在NewPersonViewController里添加一个闭包属性:
class NewPersonViewController: UIViewController { var completionHandler: ((Person) -> Void)? var person: Person? // ... 其他代码 }
修改addNewPerson方法:
extension NewPersonViewController { func addNewPerson() { guard let person = person else { return } completionHandler?(person) dismiss(animated: true) } }
然后在PeopleViewController里present时设置闭包:
func presentNewPersonVC() { let newVC = NewPersonViewController() newVC.completionHandler = { [weak self] person in guard let self = self else { return } self.selectedPerson = person self.performSegue(withIdentifier: .showPerson, sender: nil) } present(newVC, animated: true) }
这两种方式都摆脱了对视图层级的依赖,代码耦合度更低,也更易维护和扩展。同时,避免了强制解包的风险,让代码更健壮。
内容的提问来源于stack exchange,提问作者ajrlewis

