getWebdata 反向调用:大厅 observer 改永久挂钩,对齐 msext 双发行为

之前大厅 / 子游戏 observer 都 lifecycle-tied(viewWillAppear setup /
viewWillDisappear teardown),sub-game 内 overlay backgameData pop 时
只有 sub-game H5 收到 getWebdata(大厅 observer 已 teardown)。这与 msext
"NewRootVC observer 永久 + gameController observer 永久" 的双发行为不一致。

大厅 H5 可能依赖"sub-game 内 overlay 操作触发自身刷新",破坏契约。

修复:
- WebContainerViewController.subGameReturnObserver 改"永久挂钩"
  - 加 subGameReturnObserverInstalled 标志,首次 setupExternalSubscriptions
    时 install 一次,后续永不解绑(msext NewRootVC.m:593 等价)
  - teardownExternalSubscriptions 不再触碰它
- SubGameViewController.subGameReturnObserver 保持 lifecycle-tied
  - 子游戏自身 backgameData pop:sub-game 已 teardown → 仅大厅触发(单发,msext 等价)
  - 子游戏内 overlay backgameData pop:sub-game.viewWillAppear 重新 setup
    → 大厅 + sub-game 双触发(双发,msext 等价)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
joywayer
2026-06-23 23:17:39 +08:00
co-authored by Claude Opus 4.7
parent 6ef90f9c3f
commit ce3fbb7106
2 changed files with 43 additions and 19 deletions
@@ -342,8 +342,13 @@ public final class SubGameViewController: UIViewController {
bridge.call("phonestate", data: .string(state), callback: nil)
}
// overlayPhase 7pop callback getWebdata
// overlay push viewWillDisappear teardown
// overlayPhase 7pop callback getWebdata**lifecycle-tied**
// overlay push / pop viewWillDisappear
// observer "" msext
// - backgameData pop sub-game observer viewWillDisappear
// observer H5 getWebdatamsext
// - overlay backgameData popsub-game.viewWillAppear observer
// + sub-game H5 + sub-game H5 msext
subGameReturnObserver = NotificationCenter.default.addObserver(
forName: .subGameDidReturn,
object: nil,
@@ -23,8 +23,12 @@ public final class WebContainerViewController: UIViewController {
private let shakeHandler = ShakeHandler()
// MARK: - .subGameDidReturn backgameData pop callback getWebdata
//
// msext NewRootVC.m:593 viewDidLoad install
// viewWillDisappear teardown setupExternalSubscriptions
private var subGameReturnObserver: NSObjectProtocol?
private var subGameReturnObserverInstalled = false
// MARK: - Boot pipeline
//
@@ -447,18 +451,35 @@ public final class WebContainerViewController: UIViewController {
bridge.call("appservice", data: .string("2"), callback: nil)
}
// §3.2 12 getWebdata pop backgameData H5
// msext NewRootVC "backgameDatatwo" callHandler:"getWebdata"
// pop viewWillDisappear teardown
subGameReturnObserver = NotificationCenter.default.addObserver(
forName: .subGameDidReturn,
object: nil,
queue: .main
) { note in
// queue=.main closure 线 hop Swift 6 actor
let data = (note.userInfo?["data"] as? String) ?? ""
MainActor.assumeIsolated {
bridge.call("getWebdata", data: .string(data), callback: nil)
// §3.2 12 getWebdata backgameData / data H5
//
// msext NewRootVC.m:593 observer viewDidLoad
// ** listen viewWillDisappear teardown**
//
// 1. backgameData pop
// BackGameDataHandler AppCoordinator.popSubGame popViewController
// sub-game.viewWillDisappear sub-game observer sub-game lifecycle-tied
// post observer H5 getWebdatamsext
//
// 2. backgameData pop push sub-game
// OverlayViewController AppCoordinator.popOverlay popViewController
// overlay.viewWillDisappear sub-game.viewWillAppear sub-game observer
// post + sub-game observer H5 + sub-game H5
// msext NewRootVC + gameController
//
// installed viewWillAppear
if !subGameReturnObserverInstalled {
subGameReturnObserverInstalled = true
subGameReturnObserver = NotificationCenter.default.addObserver(
forName: .subGameDidReturn,
object: nil,
queue: .main
) { note in
// queue=.main closure 线 hop Swift 6 actor
let data = (note.userInfo?["data"] as? String) ?? ""
MainActor.assumeIsolated {
bridge.call("getWebdata", data: .string(data), callback: nil)
}
}
}
}
@@ -469,15 +490,13 @@ public final class WebContainerViewController: UIViewController {
// monitor closure setup
// viewWillDisappear teardown bridge/webView
//
//
// subGameReturnObserver msext NewRootVC observer
// ** teardown** setupExternalSubscriptions
BatteryMonitor.shared.onChange = nil
NetworkMonitor.shared.onChange = nil
AppLifecycleObserver.shared.onBackground = nil
AppLifecycleObserver.shared.onForeground = nil
if let token = subGameReturnObserver {
NotificationCenter.default.removeObserver(token)
subGameReturnObserver = nil
}
}
// MARK: - Alerts (terminal / retry states)