如何避免向showWindow方法传入self?Cocoa编程教程疑问
嘿,这个问题问得太到位了!作为熟悉Big Nerd Ranch《Cocoa编程(第5版)》的开发者,我可以明确告诉你:完全可以重构代码避免传入self,而且这正是iOS/Cocoa开发中优化代码耦合度的常见操作。咱们结合创建自定义ViewController的场景,一步步拆解可行的方案:
先理解为什么前期章节会传self
通常前期章节里传self,大多是为了实现代理回调、让VC获取当前控制器的依赖,或者建立双向引用。但这种写法会增加代码耦合度,甚至可能埋下循环引用的隐患——而第18章要讲的内存管理、闭包捕获正是解决这类问题的核心内容。
三种可行的重构方案
1. 用闭包代替代理(最常用的回调方式)
如果原来传self是为了让VC完成操作后通知当前控制器,完全可以用闭包代替代理模式,不需要把整个self传递给VC。
示例代码:
首先修改自定义ViewController,添加闭包属性:
class CustomViewController: UIViewController { // 定义闭包类型,根据你的业务需求调整参数 var onTaskCompleted: ((String?) -> Void)? // 在VC内部需要触发回调的地方调用闭包 func finishCurrentTask() { let result = "任务完成结果" onTaskCompleted?(result) } }
然后重构创建VC的方法,不用传self:
func createCustomViewController() -> CustomViewController { let vc = CustomViewController() // 用弱引用捕获self避免循环引用 vc.onTaskCompleted = { [weak self] result in guard let self = self else { return } // 在这里处理VC返回的结果,逻辑和原来代理方法一致 self.handleTaskResult(result) } return vc }
这种方式下,VC只持有一个闭包,而不是整个self,耦合度大幅降低,同时通过[weak self]避免了循环引用风险。
2. 依赖注入:只传递必要的对象而非整个self
如果传self是因为VC需要使用当前控制器的某些属性或服务(比如数据管理器、网络工具),那只传递VC真正需要的对象,而不是整个self。
示例代码:
假设VC需要一个数据管理器来加载数据,先修改VC:
class CustomViewController: UIViewController { // 只注入VC依赖的服务对象 var dataLoader: DataLoader! override func viewDidLoad() { super.viewDidLoad() // 使用dataLoader加载数据,不需要依赖self let data = dataLoader.fetchData() // 处理数据... } }
然后重构创建方法:
func createCustomViewController() -> CustomViewController { let vc = CustomViewController() // 只传递VC需要的dataLoader,而不是整个self vc.dataLoader = self.dataLoader return vc }
这种方式遵循了单一职责原则,VC只关注自己的业务逻辑,不需要知道当前控制器的其他属性,代码更易维护和测试。
3. 用通知中心实现无耦合的广播通信
如果VC和当前控制器的交互是单向的“事件通知”(比如VC完成操作后告知多个监听者),可以使用NotificationCenter,完全不需要传递任何对象。
示例代码:
先在VC里发送通知:
extension Notification.Name { static let customVCTaskFinished = Notification.Name("CustomVCTaskFinished") } class CustomViewController: UIViewController { func finishTask() { // 发送通知,可携带额外数据 NotificationCenter.default.post( name: .customVCTaskFinished, object: nil, userInfo: ["result": "任务完成"] ) } }
然后在当前控制器里注册监听(记得在销毁时移除):
override func viewDidLoad() { super.viewDidLoad() // 注册通知监听 NotificationCenter.default.addObserver( self, selector: #selector(handleTaskFinished(_:)), name: .customVCTaskFinished, object: nil ) } @objc func handleTaskFinished(_ notification: Notification) { if let result = notification.userInfo?["result"] as? String { // 处理通知内容 print("收到任务结果:\(result)") } } deinit { // 移除监听避免内存泄漏 NotificationCenter.default.removeObserver(self) } // 创建VC的方法完全不用传self func createCustomViewController() -> CustomViewController { return CustomViewController() }
这种方式适合低耦合的一对多场景,但要注意及时移除观察者,避免内存泄漏。
总结
这些重构方式不仅能避免传入self,还能让代码更解耦、更健壮——而第18章会深入讲解内存管理、循环引用等底层原理,帮你理解这些方案背后的设计逻辑。根据你的业务场景选择合适的方式即可:
- 一对一回调优先用闭包
- 需要传递服务/数据用依赖注入
- 一对多广播用通知中心
内容的提问来源于stack exchange,提问作者Laurence Wingo

