【问题标题】:Dependency injection in MVC, SwiftMVC、Swift 中的依赖注入
【发布时间】:2021-11-07 13:06:58
【问题描述】:

我正在按照 SOLID 原则和 MVC 架构构建一个简单的应用程序。我想在我的 NetworkClass 中解析来自服务器的图像,并在我的 ViewController 的 CollectionView 中显示它们。这是我的视图控制器:

protocol NetworkServiceProtocol {
    func parseImages() -> [Image]
}

class ViewController: UIViewController {
    
    @IBOutlet weak var imageCollection: UICollectionView!

    private let networkService: NetworkServiceProtocol
   
    convenience init(service: NetworkServiceProtocol) {
        self.init()
        self.networkService = service
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        images = networkService!.parseImages()
    }

}

这是我的 NetworkService 类(稍后我会做解析):

class NetworkService: NetworkServiceProtocol {
    
    func parseImages() -> [Image] {
        return [Image]()
    }
    
}

问题是,当我运行此代码时,它在调用networkService! 时在viewDidLoad() 中崩溃。我看到问题在于实例化 NetworkServiceProtocol。我应该在哪里以及如何实例化它?任何帮助表示赞赏!

【问题讨论】:

  • 你必须使用这个方法。由于您使用的是故事板。 stackoverflow.com/a/41926924/14733292
  • 吹毛求疵:这不是 SOLID,而是一个反例;)
  • 请您解释一下原因吗?
  • 我还要做的是在 NetworkService 中的另一个私有函数中进行解析,而不是在 parseImages() 中。然后才将我输入的内容传递给 parseImages()。
  • 我看到你定义了一个 IBOutlet。假设您正在从情节提要实例化这个 vc。在这种情况下,带有解码器的 init 被称为不是您添加的便利 init。这意味着您必须通过 segue 或在实例化后调用属性设置器以另一种方式传递网络服务。

标签: ios swift model-view-controller solid


【解决方案1】:

发生这种情况是因为您正在从情节提要初始化您的 ViewController,而情节提要正在使用您的 required init?(coder: NSCoder),它会抛出 fatalError。

为了解决它,首先改变:

    required init?(coder: NSCoder) {
            //error: networkService not initialized
            super.init(coder: coder)
        }

这个错误现在是因为networkService 类属性没有通过这个初始化器初始化。现在为了解决这个问题,你可以在类中给它一个默认属性,比如let networkService = NetworkServiceProtocol(),并在通过情节提要初始化之后将它设置为你想要的NetworkServiceProtocol对象。 (另一种选择是使其成为可选)

注意:您必须删除私有访问修饰符才能从另一个类中设置它。如果您仍然希望它是私有的,那么您必须为其编写一个 setter 方法。

【讨论】:

  • 我不能让 networkService = NetworkServiceProtocol() 因为它给出以下错误:“'NetworkServiceProtocol' 无法构造,因为它没有可访问的初始化程序”
  • 原帖确实有误导性。你不能实例化一个协议,但你可以初始化一个符合它的类。在你的情况下,它是NetworkService。
  • 哦,是的,我没有意识到这一点。
【解决方案2】:

我通过取消选中我的 View Controller 作为 Storyboard 中的 Initial View Controller 并将以下代码添加到我的 AppDelegate 中解决了这个问题:

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        let storybaord = UIStoryboard(name: "Main", bundle: Bundle.main)
        let initialVC = storybaord.instantiateViewController(withIdentifier: "ViewController") as! ViewController
        initialVC.networkService = NetworkService()
        
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.rootViewController = initialVC
        self.window?.makeKeyAndVisible()
        
        return true
    }

【讨论】:

  • 在这里,您使用NetworkServiceProtocol 作为“接口”(又名协议)。但是,接口不是抽象——嗯,不是自动的;)例如,考虑一下积压的下一个故事是否需要您现在将图像存储在 CoreData 或 Realm 中并从那里获取它们。你还会使用NetworkServiceProtocol 作为你的“图像数据提供者”吗?使用功能更灵活的“ImageProvider”协议如何:func images(with query: Query, completion: @escaping (Result<[Image], Error>) -> Void)?
  • 现在我明白你的意思了,我改一下,非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多