You need to enable JavaScript to run this app. Swift使用delegate与protocol跨控制器传值时遇到可选值解包为nil问题求助
优惠活动
大模型
产品
解决方案
定价
更多

Swift使用delegate与protocol跨控制器传值时遇到可选值解包为nil问题求助

崩溃根因排查与修复方案

核心问题定位

你遇到的found nil while unwrapping an Optional value报错,和异步网络请求、封装的ArtistViewModel本身没有关系——既然你已经验证单独打印artistViewModel?.artistName能正常获取数据,说明艺人数据是完整、非空的。
99%的触发原因是你对delegate的持有关系、赋值时机理解错位,调用的delegate属性本身是未赋值的nil,被隐式强解包触发崩溃。

分步排查逻辑

  • 第一步:定位崩溃的精确对象
    把触发崩溃的那行delegate.loadArtistViewModel(data:)代码拆开,单独打断点打印delegate本身的值:如果控制台输出nil,就能100%确认崩溃源——不是传的数据有问题,是delegate引用本身根本没有被正确赋值。
    很多新手初学delegate时的典型错误写法如下:
    // 主列表VC的错误实现
    class ArtistSearchListVC: UIViewController {
        // 错误:声明了隐式解包的delegate属性,全程没有任何地方给它赋值
        var artistViewModelDelegate: ArtistViewModelDelegate!
    
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            let currentArtist = fetchedList[indexPath.row]
            let artistVM = ArtistViewModel(artist: currentArtist)
            // 此时delegate本身是nil,强解包直接触发崩溃
            artistViewModelDelegate.loadArtistViewModel(data: artistVM)
        }
    }
    
    这种写法下,哪怕你把VM打印的再正确,只要delegate引用为空,调用方法必然崩溃。
  • 第二步:纠正delegate的正确持有关系
    delegate的核心逻辑是:持有delegate属性的对象是事件发起方,它通过自己持有的delegate引用,向遵守协议的对象索要数据、传递事件;而不是遵守协议的对象,主动调用自己持有的delegate方法去传值。
    对应你当前从列表页跳转到ArtistViewController的正向传值场景,正确的结构应该是:
    1. 在ViewModel文件里定义协议,协议必须继承AnyObject,方便后面用weak修饰避免循环引用:
    protocol ArtistViewModelDelegate: AnyObject {
        func loadArtistViewModel(data: ArtistViewModel)
    }
    
    1. 在ArtistViewController内部声明delegate属性,绝对不要写在列表页里:
    class ArtistViewController: UIViewController {
        // 必须用weak修饰,类型用可选值,不要用隐式解包
        weak var delegate: ArtistViewModelDelegate?
    }
    
    1. 列表页遵守ArtistViewModelDelegate协议,在跳转的准备环节(也就是prepare(for:sender:)方法)把自己赋值给ArtistViewController的delegate属性,等详情页加载完成后,详情页再通过delegate方法向列表页请求对应艺人数据。
  • 第三步:排查异步请求的关联影响
    你怀疑的异步Alamofire请求问题,在当前场景下不会触发这个崩溃。如果真的是网络回调未完成、数据没加载好,你打印artistName的时候就会返回nil,和你现在“能正常获取VM数据”的现象矛盾。
    唯一需要确认的是:Alamofire请求完成、刷新tableView的操作要切回主线程,避免出现cell的indexPath错位,但这个问题不会导致delegate引用为nil。
  • 第四步:优化delegate声明,避免隐式解包崩溃
    把所有delegate属性的隐式解包(以!结尾的类型)改成可选值(以?结尾的类型),调用时用可选链语法,哪怕真的出现未赋值的情况,也会跳过方法调用而不是直接崩溃:
    错误写法:
    var delegate: ArtistViewModelDelegate!
    delegate.loadArtistViewModel(data: vm)
    
    正确写法:
    weak var delegate: ArtistViewModelDelegate?
    delegate?.loadArtistViewModel(data: vm)
    

常见踩坑提醒

  • 不要给delegate不加weak修饰,否则会出现两个页面互相强引用的循环内存泄漏,甚至因为页面提前释放导致delegate引用变成nil
  • 如果是用Storyboard的Segue做页面跳转,一定要在prepare(for:sender:)方法里完成delegate的赋值,不要等页面跳转完成、详情页已经出现在屏幕上之后再回头赋值,那个时候赋值时机太晚,也会拿到nil的delegate引用
  • 对于当前“列表页传数据给详情页”的简单正向场景,其实可以直接把ArtistViewModel作为详情页的公开属性,在跳转前直接赋值,比绕一圈用delegate更简单。delegate更适合用在详情页反过来向列表页传交互事件的反向传值场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.06 04:06:03