You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xcode Organizer报Swift数组越界崩溃,求排查方案

问题分析与修复方案

核心原因

从崩溃日志可直接定位到崩溃发生在PageViewController.updateUIViewController方法中,访问context.coordinator.controllers[currentPage]时触发数组越界,根源在于:

  1. 数据不同步:Coordinator的controllers数组仅在初始化时基于当时的pages生成,当外部传入的list(即pages的来源)元素数量减少时,controllers不会自动更新,而currentPage可能仍保持在原来的较大索引,导致currentPage >= controllers.count。
  2. 无边界校验:updateUIViewController中直接使用currentPage访问数组,没有任何范围检查,一旦索引超出有效范围就会崩溃。

修复步骤

1. 同步控制器数组并添加边界校验

修改PageViewController的updateUIViewController方法,确保controllers始终与最新的pages一致,并对currentPage做安全处理:

func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
    // 同步controllers数组,匹配最新的pages数量
    if context.coordinator.controllers.count != pages.count {
        context.coordinator.controllers = pages.map { UIHostingController(rootView: $0) }
    }
    
    // 计算安全的当前页码,确保在有效范围内
    let safePage = min(max(currentPage, 0), context.coordinator.controllers.count - 1)
    // 如果页码被修正,同步更新绑定的currentPage
    if safePage != currentPage {
        currentPage = safePage
    }
    
    // 仅当数组非空时执行页面切换
    if !context.coordinator.controllers.isEmpty {
        pageViewController.setViewControllers(
            [context.coordinator.controllers[safePage]], 
            direction: .forward, 
            animated: true
        )
    }
}

2. 优化BannerView的页码切换逻辑

在nextPage方法中增加空数组判断,避免空数组时出现无效计算:

func nextPage() -> Void {
    guard !list.isEmpty else { return }
    currentPage = currentPage == list.count - 1 ? 0 : currentPage + 1
}

3. 处理空数组场景

在BannerView的body中,当list为空时返回占位视图,避免创建无意义的PageViewController:

var body: some View {
    Group {
        if list.isEmpty {
            // 替换为你需要的空状态占位视图
            Text("暂无横幅内容")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        } else {
            ZStack(alignment: .bottom) {
                PageViewController(
                    pages: list.map(BannerCard.init),
                    currentPage: $currentPage,
                    onChange: {
                        timer?.invalidate()
                        timer = nil
                        restLoop()
                    }
                )
                PageControl(numberOfPages: list.count, currentPage: $currentPage)
                    .frame(width: CGFloat(list.count * 18))
            }
            .onAppear(perform: restLoop)
        }
    }
}

额外建议

  • 所有数组下标访问前都必须做边界校验,或使用indices.contains(_:)判断索引有效性:
    if context.coordinator.controllers.indices.contains(currentPage) {
        // 安全访问数组元素
    }
    
  • 避免在Coordinator中缓存依赖外部可变数据的内容,尽量在updateUIViewController中保持数据最新。

内容的提问来源于stack exchange,提问作者Ethan Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:12:10