What does Closure.cleaner (func) mean in Spark?

为解决Scala构造闭包时捕获过多外部变量的问题,Spark引入了闭包清理机制。该机制可在运行时准确地移除不必要的引用,从而避免网络带宽浪费并确保闭包能被正确序列化。

Ankur Dave, Spark committer at UCB AMPLab
Answered Nov 26, 2013

When Scala constructs a closure, it determines which outer variables the closure will use and stores references to them in the closure object. This allows the closure to work properly even when it’s called from a different scope than it was created in.

Scala sometimes errs on the side of capturing too many outer variables (see SI-1419). That’s harmless in most cases, because the extra captured variables simply don’t get used (though this prevents them from getting GC’d). But it poses a problem for Spark, which has to send closures across the network so they can be run on slaves. When a closure contains unnecessary references, it wastes network bandwidth. More importantly, some of the references may point to non-serializable objects, and Spark will fail to serialize the closure.

To work around this bug in Scala, the ClosureCleaner traverses the object at runtime and prunes the unnecessary references. Since it does this at runtime, it can be more accurate than the Scala compiler can. Spark can then safely serialize the cleaned closure.

这个错误是因为 `reloadMessages()` 方法的定义 **不接受闭包参数**,但你在调用时传递了一个尾随闭包 `{ [weak self] in ... }`。Swift 编译器会提示 `Extra trailing closure passed in call`(多余的尾随闭包)。 --- ### 解决方法: #### 1. **检查 `TPGuardMessageListView.reloadMessages()` 的定义** 如果 `reloadMessages()` **没有闭包参数**,你需要移除尾随闭包: ```swift private func loadDemoData() { messageListView.deviceShareMessages = demoMessages messageListView.reloadMessages() // 移除闭包 messageListView.isHidden = false // 直接操作 updateListHeight(messageListView) // 直接调用 messageListView2.deviceShareMessages = demoMessages2 messageListView2.reloadMessages() // 移除闭包 messageListView2.isHidden = false updateListHeight(messageListView2) } ``` #### 2. **如果 `reloadMessages()` 需要异步回调** 如果 `reloadMessages()` 是异步操作(例如从网络加载数据),你需要 **修改它的定义** 以接受闭包: ```swift // 在 TPGuardMessageListView 中修改方法签名 func reloadMessages(completion: (() -> Void)? = nil) { // 异步加载数据... DispatchQueue.main.async { self.reloadData() // 假设是刷新表格 completion?() // 回调 } } ``` 然后调用方式就是正确的: ```swift messageListView2.reloadMessages { [weak self] in self?.messageListView2.isHidden = false self?.updateListHeight(self?.messageListView2) } ``` #### 3. **如果 `reloadMessages()` 是同步操作** 如果 `reloadMessages()` 是同步的(直接刷新 UI),但你想在刷新后执行操作,可以用 `DispatchQueue.main.async` 延迟执行: ```swift messageListView2.reloadMessages() // 同步调用 DispatchQueue.main.async { [weak self] in self?.messageListView2.isHidden = false self?.updateListHeight(self?.messageListView2) } ``` --- ### 关键点: - **错误原因**:调用方法时传递了闭包,但方法本身不支持闭包参数。 - **解决方案**: 1. **移除闭包**(如果方法是同步的)。 2. **修改方法签名**(如果需要异步回调)。 3. **用 `DispatchQueue.main.async` 延迟操作**(如果方法是同步但需要后续处理)。 --- ### 修复后的代码示例: 假设 `reloadMessages()` 是同步的,修正后的 `loadDemoData()`: ```swift private func loadDemoData() { // 加载第一个列表 messageListView.deviceShareMessages = demoMessages messageListView.reloadMessages() // 无闭包 messageListView.isHidden = false updateListHeight(messageListView) // 加载第二个列表 messageListView2.deviceShareMessages = demoMessages2 messageListView2.reloadMessages() // 无闭包 messageListView2.isHidden = false updateListHeight(messageListView2) } ``` 如果 `reloadMessages()` 是异步的,确保它的定义包含闭包参数: ```swift // TPGuardMessageListView.swift func reloadMessages(completion: @escaping () -> Void) { // 异步加载数据... DispatchQueue.main.async { self.reloadData() // 刷新表格 completion() // 回调 } } ``` --- ### 其他建议: - 使用 `[weak self]` 时,注意可选链(`self?.`)可能导致某些操作未执行。 - 如果 `updateListHeight()` 依赖动态计算的高度,确保 `TPGuardMessageListView` 在数据加载完成后能正确计算高度(例如通过 `invalidateIntrinsicContentSize()`)。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值