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

Swift音乐播放器播放专辑触发NSRangeException异常求助

问题原因分析与解决方案

这个崩溃的核心问题在于你复用了同一个MPMediaQuery实例,并且每次点击都叠加过滤条件,导致第二次查询时没有匹配到任何媒体项:

  • 第一次点击播放专辑时,你给albumQuery添加了对应专辑的过滤条件,查询到了正确的歌曲集合;
  • 第二次点击另一张专辑时,你又给同一个albumQuery添加了新的过滤条件——这时候查询的是同时满足两张专辑标题的歌曲,自然没有结果,self.albumQuery.items变成了空数组,后续访问myCollectionToPlay.items[0]就触发了越界异常。

另外,你直接强制解包self.albumQuery.items!也是风险点,即使查询正常,也可能因为权限或无匹配项导致崩溃。


修正后的代码

有两种可靠的修复方式,推荐第一种(每次创建新的查询实例,避免状态污染):

方式一:每次创建新的MPMediaQuery实例

let audioPlayer = MPMusicPlayerController.systemMusicPlayer

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let play = UITableViewRowAction(style: .normal, title: "Play") { action, indexPath in
        // 每次点击都创建新的专辑查询实例,避免旧的过滤条件残留
        let albumQuery = MPMediaQuery.albums()
        let albumTitle = self.allMusicInfo[indexPath.section].albums[indexPath.row].albumTitle
        
        albumQuery.addFilterPredicate(MPMediaPropertyPredicate(
            value: albumTitle,
            forProperty: MPMediaItemPropertyAlbumTitle,
            comparisonType: .equalTo
        ))
        
        // 安全处理查询结果,避免强制解包
        guard let albumItems = albumQuery.items, !albumItems.isEmpty else {
            print("没有找到该专辑的歌曲")
            return
        }
        
        let myCollectionToPlay = MPMediaItemCollection(items: albumItems)
        self.audioPlayer.setQueue(with: myCollectionToPlay)
        // 其实不需要手动设置nowPlayingItem,setQueue后默认会从第一首开始播放
        // self.audioPlayer.nowPlayingItem = myCollectionToPlay.items[0]
        self.audioPlayer.prepareToPlay()
        self.audioPlayer.play()
    }
    return [play]
}

方式二:清空旧的过滤条件后再添加新条件

如果你坚持复用原来的albumQuery,需要在每次添加新条件前清空所有已有的过滤条件:

let albumQuery: MPMediaQuery = MPMediaQuery.albums()
let audioPlayer = MPMusicPlayerController.systemMusicPlayer

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let play = UITableViewRowAction(style: .normal, title: "Play") { action, indexPath in
        let albumTitle = self.allMusicInfo[indexPath.section].albums[indexPath.row].albumTitle
        
        // 清空之前所有的过滤条件
        self.albumQuery.filterPredicates.removeAll()
        
        self.albumQuery.addFilterPredicate(MPMediaPropertyPredicate(
            value: albumTitle,
            forProperty: MPMediaItemPropertyAlbumTitle,
            comparisonType: .equalTo
        ))
        
        guard let albumItems = self.albumQuery.items, !albumItems.isEmpty else {
            print("没有找到该专辑的歌曲")
            return
        }
        
        let myCollectionToPlay = MPMediaItemCollection(items: albumItems)
        self.audioPlayer.setQueue(with: myCollectionToPlay)
        self.audioPlayer.prepareToPlay()
        self.audioPlayer.play()
    }
    return [play]
}

额外提示

  • 调用setQueue(with:)后,系统音乐播放器默认会从集合的第一首歌开始播放,所以你可以去掉self.audioPlayer.nowPlayingItem = myCollectionToPlay.items[0]这行代码,进一步避免越界风险;
  • 始终用guard或if let安全处理可选值,避免强制解包!导致的崩溃;
  • 记得检查用户是否授予了媒体库访问权限,没有权限的话查询也会返回空结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:55:21