Swift MVC下Dependency injection注入NetworkService调用崩溃如何解决
问题根因
你的崩溃核心是networkService属性未完成初始化就被强制解包:
- 如果你通过Storyboard/XIB加载ViewController,系统会调用
init(coder:)构造器,不会走你自定义的convenience init(service:),导致不可选类型的networkService实际为nil,使用!强制解包直接触发崩溃 - 你定义的
networkService是不可选的let属性,没有在指定构造器中完成赋值,本身就不符合Swift内存安全要求
解决方案
根据你加载ViewController的方式二选一即可:
方案1:Storyboard/XIB加载场景(属性注入)
修改ViewController代码,改用属性注入传递服务实例,去掉强制解包:
protocol NetworkServiceProtocol { func parseImages() -> [Image] } class ViewController: UIViewController { @IBOutlet weak var imageCollection: UICollectionView! // 改为可选类型,避免强制解包 private var networkService: NetworkServiceProtocol? // 补全你遗漏的images属性定义 private var images: [Image] = [] // 提供属性注入方法 func inject(service: NetworkServiceProtocol) { self.networkService = service } override func viewDidLoad() { super.viewDidLoad() // 用可选绑定安全取值 guard let service = networkService else { return } images = service.parseImages() // 刷新列表展示图片 imageCollection.reloadData() } }
在需要创建ViewController的位置(如上一层页面跳转逻辑、AppDelegate设置根控制器)完成实例化和注入:
// 从Storyboard加载目标ViewController guard let vc = storyboard?.instantiateViewController(withIdentifier: "你的ViewController在Storyboard里的ID") as? ViewController else { return } // 实例化NetworkService并注入 vc.inject(service: NetworkService()) // 执行跳转/设置根控制器逻辑 navigationController?.pushViewController(vc, animated: true)
方案2:纯代码加载场景(构造器注入)
重写指定构造器,保证networkService在构造阶段就完成赋值:
class ViewController: UIViewController { @IBOutlet weak var imageCollection: UICollectionView! private let networkService: NetworkServiceProtocol private var images: [Image] = [] // 自定义指定构造器,将服务作为必填参数 init(service: NetworkServiceProtocol) { self.networkService = service super.init(nibName: nil, bundle: nil) } // 如果你不会用XIB/Storyboard加载该页面,可直接触发错误避免误用 required init?(coder: NSCoder) { fatalError("该页面不支持从XIB/Storyboard加载") } override func viewDidLoad() { super.viewDidLoad() // 不需要解包,属性在构造阶段已经完成赋值 images = networkService.parseImages() imageCollection.reloadData() } }
实例化时直接传递服务实例即可:
let service = NetworkService() let vc = ViewController(service: service) navigationController?.pushViewController(vc, animated: true)
注意事项
- 不要对无取值把握的可选值使用强制解包
!,优先用if let/guard let做安全校验 - 服务实例的创建逻辑放在ViewController外部,符合SOLID的依赖倒置原则,后续要替换测试用的Mock服务时无需修改ViewController代码
- 拿到接口返回的图片数组后,必须调用
UICollectionView的reloadData方法触发界面刷新
内容的提问来源于stack exchange,提问作者MaryLitv21
相关产品推荐
相关产品推荐

