CoderMJLee / MJRefresh

An easy way to use pull-to-refresh.
MIT License
13.8k stars 3.55k forks source link

SwiftUI KVO Crash #1589

Open LuckyCat7848 opened 1 year ago

LuckyCat7848 commented 1 year ago

必现/偶发? 必现

怎么样重现这个bug

  1. 把 MJRefreshFooter 使用 UIViewRepresentable 包装成 SwiftUI 中可以使用的 View;
  2. 在 SwiftUI 页面使用加载,页面返回销毁。此时崩溃在 MJRefreshComponent line 105 removeObservers() 方法中。 崩溃日志: *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <MJRefreshBackNormalFooter 0x126b7e7b0> for the key path "contentOffset" from <_TtGC7SwiftUI16PlatformViewHostGVS_P10$1c8b6b12832PlatformViewRepresentableAdaptorV9JKSwiftUI26SwiftUIMJRefreshFooterView__ 0x126b7f3d0> because it is not registered as an observer.'

分析原因:

  1. 在这里 self.superview 移除KVO监听时,移除的是 contentOffset 和 contentSize,而SwiftUI场景走到这里的 self.superView 不是UIScrollView的子类,没有添加这两个属性及监听,因此崩溃。
    • (void)removeObservers { [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset]; [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize]; [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState]; self.pan = nil; }

解决建议:

  1. 这里移除KVO的时候判断一下: if (self.superview && ![self.superview isKindOfClass:[UIScrollView class]]) return;

运行环境