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
相关产品推荐
相关产品推荐

