You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免向showWindow方法传入self?Cocoa编程教程疑问

如何重构代码避免传入self来创建自定义ViewController?

嘿,这个问题问得太到位了!作为熟悉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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:33:56