例如:
VC1 -> VC2 -> VC3 -> VC4
VC5 -> VC6 -> VC7 -> VC8
假设VC4和VC8都需要持有当前用户的UserService。
请注意,我们希望避免使用Singleton。
有没有一种优雅的方式来处理这种DI情况呢?
经过一些研究,我发现有人提到了"Abstract Factory"、"Context interfaces"、"Builder"和"strategy pattern"等方法。
但是我找不到在iOS上应用它们的示例。
好的,我会尝试一下。
你说“不使用单例模式”,所以我在以下内容中排除了它,但请参阅答案底部。
Josh Homann的评论已经指出了一个解决方案,但是我个人对协调器模式有些困惑。
正如Josh所说,视图控制器不应该(太多地)相互了解[1],但是如何传递/访问依赖项,例如协调器?有几种模式可以解决这个问题,但大多数都存在一个问题,即违反了您的要求:它们更多地使协调器成为单例(自身或作为另一个单例的属性,如AppDelegate
)。协调器通常也是事实上的单例(但并不总是,也没有必要这样)。
我倾向于依赖于简单初始化的属性或(最常见的)延迟属性和面向协议的编程。让我们构建一个示例:UserService
将是定义服务所有功能的协议,MyUserService
是其实现结构体。假设UserService
是一个设计构造,基本上作为一种获取器/设置器系统用于某些用户相关数据:访问令牌(例如保存在钥匙串中),一些偏好设置(头像图像的URL)等。在初始化时,MyUserService
还准备数据(例如从远程加载)。这将在多个独立的屏幕/视图控制器中使用,并且不是单例。
现在,每个希望访问此数据的视图控制器都有一个简单的属性:
lazy var userService: UserService = MyUserService()
特别是如果您不能按照我上面概述的方式设计它,即如果您需要具有内存中的单个状态数据,那么单例基本上是一种简单且适当的实现方式。 (即使使用(惰性)属性也很有益处,您的视图控制器甚至不需要知道它是单例还是不是,您仍然可以分别进行存根/模拟(即不仅限于全局实例)。)
protocol UserServiceProtocol {
func login(user: String, password: String) -> Bool
func logout()
var loggedInUser: User? //where User is some model you define
}
UserService
类。AppDelegate
中创建并保持实例,并通过一系列的VC传递它。UserService
并将其传递给VC4和VC8。VC4和VC8会有一个var userService: UserServiceProtocol?
。UserService
需要从外部源恢复其状态。这样,即使VC4和VC8有不同的对象实例,状态也将相同。private lazy var service: SomeService = SomeService()
这是不成问题的。我更喜欢需要 SomeService
实例的 init 或者至少(对于 ViewControllers 来说更容易):
var service: SomeService!
这是我在几个项目中使用的一种方法,可能会对您有所帮助。
这里有一个简单的例子:
struct ViewControllerFactory {
private let userService: UserServiceProtocol
init(userService: UserServiceProtocol) {
self.userService = userService
}
// This VC needs the user service
func makeVC4() -> VC4 {
let vc4 = VC4(userService: userService)
return vc4
}
// This VC does not
func makeVC5() -> VC5 {
let vc5 = VC5()
}
// This VC also needs the user service
func makeVC8() -> VC8 {
let vc8 = VC8(userService: userService)
return vc8
}
}
ViewControllerFactory对象可以被实例化并存储在AppDelegate中。
这就是基础。此外,我还会看一下以下内容(还可以参考其他答案,这里提供了一些不错的建议):
我发现协调器/路由器设计模式最适合注入依赖和处理应用程序导航。看看这篇文章,它对我帮助很大 https://medium.com/@dkw5877/flow-coordinators-333ed64f3dd
我尝试解决这个问题,并在此处上传了一个示例架构:https://github.com/ivanovi/DI-demo
为了使它更清晰,我使用了三个VC简化了实现,但是该解决方案适用于任何深度。视图控制器链如下:
Master -> Detail -> MoreDetail(其中注入了依赖项)
所提出的架构有四个构建块:
Coordinator Repository:包含所有协调器和共享状态。注入所需的依赖项。
ViewController Coordinator:执行导航到下一个ViewController。协调器持有一个工厂,该工厂生成所需的下一个VC实例。
ViewController工厂:负责初始化和配置特定的ViewController。通常由协调器拥有并由CoordinatorRepository注入到协调器中。
ViewController:要在屏幕上呈现的ViewController。
N.b.:在示例中,我返回新创建的VC实例只是为了产生示例 - 即在实际实现中不需要返回VC。
希望能对您有所帮助。
let viewController = CustomViewController()
viewController.data = NSObject() //some data object
navigationController.show(viewController, sender: self)
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var appCoordinator:AppCoordinator?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController()
appCoordinator = AppCoordinator(with: window?.rootViewController as! UINavigationController)
appCoordinator?.start()
window?.makeKeyAndVisible()
return true
}
}
UserService
,所以在整个流程中传递它以仅在这两个子级上使用似乎对我来说不是最优的选择,特别是当它有两到三个依赖项时。 - iOSGeek