1. 主页 > 大智慧

Swift高效实现类间通信的5个技巧与最佳实践

你是不是经常遇到这种情况?明明在A页面点了收藏按钮,B页面的小红点死活不更新。就像新手如何快速涨粉却总找不到突破口一样,Swift的类间通信问题能把人逼疯——昨天写的代码今天跑起来像中了邪,这个按钮点了没反应,那个数据死活传不过去...

(抓头发)别慌,这事儿我当初也经历过。记得第一次做购物车功能,结算页面的价格死活同步不到订单列表,差点被产品经理追杀三条街。后来才发现是类之间通信姿势不对...

一、直接调用法:最笨但有效

刚入门那会儿,我只会这么干:在ClassA里创建ClassB的实例,然后直接调用方法。好比邻居家老王要借酱油,翻墙过来拿——简单粗暴但容易出事。

swift复制
class OrderManager {
    func updatePrice() {
        let cart = ShoppingCart()
        cart.refreshTotal()
    }
}

??致命问题??:这两个类会变成连体婴,改个方法名整个项目崩给你看。适合临时测试用,正式项目千万别这么玩!

二、代理模式:像订外卖一样精准

后来学会了这招,就像点外卖指定送餐地址。这里有个坑:weak引用不加的话,内存泄漏分分钟教你做人。

swift复制
protocol PaymentDelegate: AnyObject {
    func paymentDidComplete(orderID: String)
}

class CheckoutVC {
    weak var delegate: PaymentDelegate?
    
    private func paySuccess() {
        delegate?.paymentDidComplete(orderID: "666")
    }
}

// 在订单页面绑定
extension OrderVC: PaymentDelegate {
    func paymentDidComplete(orderID: String) {
        print("更新订单状态:\(orderID)")
    }
}

昨天实习生问我:"为啥要加weak?" 这问题当年我也栽过跟头。想象一下,两个对象互相抓着对方衣领不放,谁都不肯先松手——这就是循环引用,结果就是内存爆炸。

三、闭包回调:临时工的好去处

遇到那种一次性的数据传递,用闭包最合适。但!要!注!意!(敲黑板)[weak self]不写的话,页面关了回调还在跑,crash日志能凑本小说。

swift复制
class DataLoader {
    var onComplete: ((String) -> Void)?
    
    func fetchData() {
        // 模拟网络请求
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.onComplete?("新鲜出炉的数据")
        }
    }
}

// 使用时
let loader = DataLoader()
loader.onComplete = { [weak self] data in
    self?.showData(data)
}

上次见人忘了写weak,页面关了还在后台偷偷干活,手机烫得能煎鸡蛋。你们说,这种坑爹事谁顶得住?

四、通知中心:小区广播站

适合多个地方要同步的情况,比如用户登录状态变更。但通知名最好统一定义,不然到处乱发通知,调试时想死的心都有。

swift复制
extension Notification.Name {
    static let userDidLogin = Notification.Name("USER_LOGIN_EVENT")
}

class AuthService {
    func login() {
        NotificationCenter.default.post(name: .userDidLogin, object: nil)
    }
}

// 各个页面监听
NotificationCenter.default.addObserver(self, selector: #selector(handleLogin), name: .userDidLogin, object: nil)

有个冷知识:iOS系统自己就用了200多个通知,乱发通知可能会和系统通知撞车。所以命名时加个前缀最稳妥,比如"MyApp_UserUpdate"

五、单例模式:共享储物柜

全局要用的东西,比如用户配置、网络状态,用单例最方便。但别滥用,不然代码会变成一坨浆糊。

swift复制
class AppSettings {
    static let shared = AppSettings()
    private init() {}
    
    var themeColor: UIColor = .blue
}

// 任何地方都能访问
AppSettings.shared.themeColor = .red

上周碰到个奇葩bug:两个地方同时修改单例对象的值,数据错乱得像被猫抓过的毛线团。所以关键数据要加线程锁,不过这是后话了...


??灵魂拷问时间??:这么多方法该用哪个?我做了个对比表:

方法适用场景内存风险代码量
直接调用临时测试
代理模式1对1精确通信
闭包回调异步结果回调
通知中心1对多广播
单例模式全局状态管理

有次面试被问到:"KVO和通知中心有什么区别?" 当场懵逼。后来想通了,KVO像是给特定对象装了监控摄像头,通知中心则是小区广播——一个盯人,一个广播。

新手最容易踩的三大坑:

  1. 循环引用导致内存泄漏(weak是保命符)
  2. 通知监听不移除(iOS 9之后其实可以不手动移除了)
  3. 单例滥用让代码难以维护(这玩意用多了会上瘾)

最近在重构三年前的老项目,看见前辈写的通信代码差点吐血——某个详情页居然用通知传了18个参数,接收方要像拆炸弹一样解析数据。所以说啊,方法没有好坏,只有合不合适。

有个冷门但好用的技巧:类型方法配合闭包。比如:

swift复制
class Router {
    static var onSearch: ((String) -> Void)?
}

// A页面设置
Router.onSearch = { keyword in
    // 跳转搜索页
}

// B页面触发
Router.onSearch?("打折iPhone")

这种方法适合模块间解耦,但要注意生命周期管理。用好了能让代码清爽得像刚洗完澡的萨摩耶。

说真的,类间通信就像谈恋爱,太粘了(高耦合)要完蛋,太冷淡(完全隔离)也过不下去。找到那个平衡点,代码才能长治久安。我现在写代码,宁可多写几行注释也不愿走捷径——三个月后的自己看代码时,会感谢现在不偷懒的自己。

本文由嘻道妙招独家原创,未经允许,严禁转载