iOS开发给UILabel赋值触发Unexpectedly found nil崩溃排查
触发"Unexpectedly found nil"崩溃的原因
你出现崩溃的核心原因是访问未完成加载的IBOutlet属性:
- 从Storyboard初始化
DetailViewController实例时,控制器不会立刻加载视图层级,所有通过@IBOutlet关联的UI控件(包括你关联的name标签)此时都是未连接状态,值为nil - 只有当控制器视图即将被展示(比如执行push操作触发系统加载视图)时,才会完成控件和IBOutlet的连接
- 你的代码在
pushViewController执行后立刻访问vc.name.text,此时name标签还是nil,而@IBOutlet默认是隐式解包可选类型,直接访问nil值的属性就会触发崩溃 - 额外注意:你当前代码里把
vc.detailVCTitle赋值写在push之后的写法虽然不会触发崩溃,但不符合控制器传值的常规逻辑,建议统一在push前完成所有数据传递。
修复方法
不要在外部直接操作目标控制器的UI控件,遵循「外部传数据、控制器自己管理UI」的原则修改:
- 给
DetailViewController新增一个存储展示次数的属性,在viewDidLoad(此时所有IBOutlet已经完成连接,不会为nil)中给标签赋值
class DetailViewController: UIViewController { @IBOutlet var imageView: UIImageView! @IBOutlet var name: UILabel! var selectedImage : String? var detailVCTitle: String? // 新增:存储图片展示次数 var displayCount: Int? override func viewDidLoad() { super.viewDidLoad() title = detailVCTitle navigationItem.largeTitleDisplayMode = .never if let imageToLoad = selectedImage { imageView.image = UIImage(named: imageToLoad) } // 视图加载完成后再给label赋值,此时name已连接,不会为nil if let count = displayCount { name.text = "It was shown \(count) times" } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.hidesBarsOnTap = true } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.hidesBarsOnTap = false } }
- 修改列表点击方法,移除直接给label赋值的代码,改为给新增的
displayCount属性传值,所有传值操作放在push执行前:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController { counter += 1 vc.selectedImage = pictures[indexPath.row] vc.detailVCTitle = "Picture \(indexPath.row + 1) of \(pictures.count)" // 只传递数据,不直接操作控制器的UI控件 vc.displayCount = counter navigationController?.pushViewController(vc, animated: true) } }
不推荐的临时方案:如果一定要在push前操作UI控件,可以在赋值前调用
vc.loadViewIfNeeded()强制提前加载视图、连接IBOutlet,但这种写法会破坏代码封装性,后续维护很容易出问题。
内容的提问来源于stack exchange,提问作者Nikita Chechnev
相关产品推荐
相关产品推荐

