iOS 设计模式 之 Handler(处理者)

106 阅读3分钟

在 iOS 开发中,Handler(处理者)  是一种常见的设计模式,用于解耦代码逻辑、异步处理任务或事件回调。它通常以闭包(Closure)、代理(Delegate)或目标-动作(Target-Action)的形式实现,帮助开发者将复杂逻辑拆分为可复用的模块。以下是几个典型场景的代码示例和优化思路:


1. 闭包(Closure)作为 Handler

场景:网络请求完成后,通过闭包回调处理结果,避免阻塞主线程。

优化前(嵌套回调,代码臃肿)


func fetchUserData(completion: @escaping (User?, Error?) -> Void) {
    URLSession.shared.dataTask(with: URL(string: "https://api.example.com/user")!) { data, _, error in
        if let error = error {
            completion(nil, error)
            return
        }
        guard let data = data else {
            completion(nil, NSError(domain: "InvalidData", code: 0))
            return
        }
        do {
            let user = try JSONDecoder().decode(User.self, from: data)
            completion(user, nil)
        } catch {
            completion(nil, error)
        }
    }.resume()
}
 
// 调用时嵌套回调
fetchUserData { user, error in
    if let error = error {
        print("Error: (error)")
    } else if let user = user {
        print("User: (user.name)")
    }
}

优化后(提取 Handler 逻辑)


// 定义通用的 Result Handler
typealias ResultHandler<T> = (Result<T, Error>) -> Void
 
func fetchUserData(completion: @escaping ResultHandler<User>) {
    let url = URL(string: "https://api.example.com/user")!
    URLSession.shared.dataTask(with: url) { data, _, error in
        let result: Result<User, Error> = Result {
            guard let data = data else { throw error ?? NSError(domain: "Unknown", code: 0) }
            return try JSONDecoder().decode(User.self, from: data)
        }
        DispatchQueue.main.async { completion(result) } // 切换到主线程
    }.resume()
}
 
// 调用时更清晰
fetchUserData { result in
    switch result {
    case .success(let user): print("User: (user.name)")
    case .failure(let error): print("Error: (error)")
    }
}

优化点

  • 使用 Result 类型统一处理成功/失败,减少嵌套。
  • 提取 ResultHandler 类型别名,提升可读性。
  • 确保回调在主线程执行(UI 更新安全)。

2. 代理(Delegate)作为 Handler

场景:表格视图(UITableView)的数据源和事件处理通过代理解耦。

优化前(直接实现代理方法,逻辑分散)


class ViewController: UIViewController, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = "Row (indexPath.row)"
        return cell
    }
}

优化后(提取 Handler 类)


// 定义 Handler 协议
protocol TableViewHandlerDelegate: AnyObject {
    func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath)
}
 
class TableViewHandler: NSObject, UITableViewDataSource {
    weak var delegate: TableViewHandlerDelegate?
    private let rowCount: Int
    
    init(rowCount: Int = 10) {
        self.rowCount = rowCount
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return rowCount
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        delegate?.configureCell(cell, at: indexPath)
        return cell
    }
}
 
// 在 ViewController 中使用
class ViewController: UIViewController, TableViewHandlerDelegate {
    @IBOutlet weak var tableView: UITableView!
    private let handler = TableViewHandler(rowCount: 10)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = handler
        handler.delegate = self
    }
    
    func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath) {
        cell.textLabel?.text = "Optimized Row (indexPath.row)"
    }
}

优化点

  • 将数据源逻辑移至 TableViewHandler,减少 ViewController 的职责。
  • 通过协议(Delegate)灵活定制单元格配置。
  • 便于单元测试(可单独测试 TableViewHandler)。

3. 目标-动作(Target-Action)作为 Handler

场景:按钮点击事件通过 @objc 方法处理,但可优化为闭包。

优化前(传统 Target-Action)


class ViewController: UIViewController {
    @objc func buttonTapped() {
        print("Button tapped!")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(type: .system)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    }
}

优化后(闭包封装)


extension UIButton {
    func onTap(_ handler: @escaping () -> Void) {
        self.addTarget(self, action: #selector(handleTap), for: .touchUpInside)
        // 使用关联对象存储闭包(需导入 Objective-C 运行时)
        objc_setAssociatedObject(self, &AssociatedKeys.tapHandler, handler, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    @objc private func handleTap() {
        if let handler = objc_getAssociatedObject(self, &AssociatedKeys.tapHandler) as? () -> Void {
            handler()
        }
    }
}
 
private struct AssociatedKeys {
    static var tapHandler = "tapHandler"
}
 
// 使用
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(type: .system)
        button.onTap { print("Closure-based tap handler!") }
    }
}

优化点

  • 避免 @objc 方法污染 ViewController
  • 直接通过闭包定义事件逻辑,更符合 Swift 风格。

总结

Handler 类型适用场景优化收益
闭包(Closure)异步回调、轻量级事件处理减少嵌套,统一错误处理
代理(Delegate)复杂交互、多方法协作解耦逻辑,提升可测试性
目标-动作(闭包封装)UI 事件处理避免 @objc,代码更简洁

通过合理使用 Handler 模式,可以显著提升代码的可读性、可维护性和复用性,尤其在异步编程和模块化设计中效果显著