您有许多选项都可以追溯到确保 Class1 和 Class2 使用“共享对象”的相同实例。
- 明确将同一个实例从外部传递给Class1和Class2,而不是让Class1和Class2自己创建实例;或
- 为 Functions 类提供一个单例初始化器,然后确保 Class1 和 Class2 使用它;或
- 使用一种称为注册表模式的设计模式,并确保 Class1 和 Class2 从注册表中获取其函数类的实例;或
- 使用依赖注入(复杂)。
场景 (1) 是最容易理解的,因为它完全是这样的:
Functions *funcs = [[Functions alloc] init];
Class1 *obj1 = [[Class1 alloc] initWithFunctions:funcs];
Class2 *obj2 = [[Class2 alloc] initWithFunctions:funcs];
/* or alternatively use setters after initialization */
场景 (2) 是第二简单的并且非常常见。 Cocoa 为它的许多类提供了单例初始化器。每当您看到 +shared... 作为初始化器的前缀时,它可能会返回一个单例。
@implementation Functions
+(id)sharedFunctions {
static Functions *sharedFunctions = nil;
@synchronized(self) {
if (sharedFunctions == nil) {
sharedFunctions = [[self alloc] init];
}
return sharedFunctions;
}
}
@end
静态变量仅初始化一次,这意味着您可以通过检查其初始值(仅发生一次)将对象的实例延迟加载到其中。对该方法的每次后续调用都返回相同的实例。
Functions *f1 = [Functions sharedFunctions];
Functions *f2 = [Functions sharedFunctions];
/* f1 == f2; */ // always
与其他语言相比,选项 (3) 在 Objective-C 中的使用并不多,它实现的目标与单例几乎相同。这个想法是你有一个可以按键查找的对象字典。需要访问注册表中项目的所有内容都只向注册表询问其自己的实例。通常注册表本身是一个单例。
选项(4),依赖注入,实际上是一个非常优雅的解决方案,具有许多好处,但代价是增加了复杂性。好处来自您确保依赖关系始终松散耦合的事实(这使得交换实现和独立单元测试依赖关系变得更加简单)。复杂性来自于检索您需要的实例的非标准机制。
依赖注入围绕着没有对象实例化自己的依赖的想法。而不是依靠其他东西来提供这些依赖关系。那个“其他东西”被称为依赖注入容器。依赖注入容器实际上是注册表模式之上的一层,因为容器将保存依赖项的预构建实例,或者知道如何实例化新实例。
在最简单的情况下,依赖注入正是我在选项(1)中演示的。你甚至不需要依赖注入容器来实现这一点。
为了提高复杂性,依赖注入引入了 DI 容器的概念,它封装了许多现有的设计模式(注册表,如选项 (3) 所示,单例(可能,但不严格)和工厂(知道如何创建它管理的对象的新实例)。
演示 DI 容器的完整实现可能超出了本问题的范围,但从公共接口的角度来看,一个实现可能如下所示:
DIContainer *container = [DIContainer sharedContainer];
[container registerClass:[ClassA class]];
[container registerClass:[ClassB class]];
[container registerDependentProperty:@selector(setClassA:)
withInstanceOf:[ClassA class]
forClass:[ClassB class]];
ClassB *obj = [container makeInstanceOfClass:[ClassB class]];
NSLog(@"ClassB's -classA type = %@", [[obj classA] class]);
我只是在这篇文章的中间打了这个字,所以不要认为它是 100% 准确的,但你明白了这个概念。容器已被指示,当它初始化 ClassB 的实例时,它必须调用 -setClassA:,使用 ClassA 的实例,它还根据容器中定义的规则进行初始化(在这种情况下,没有ClassA 所以它只返回一个普通的实例。
如果您对这个答案没有更多了解,请记住选项 (1) 和 (2) ;)