【问题标题】:How can I implement the service locator pattern in Cocoa Touch across multiple projects?如何跨多个项目在 Cocoa Touch 中实现服务定位器模式?
【发布时间】:2011-03-26 03:26:33
【问题描述】:

这是一个困扰我一段时间的问题。我对其中一些模式还是很陌生,所以如果我错误地使用了任何术语,你必须原谅我(并纠正我)。

我的方法

我创建了一个游戏引擎。我的游戏引擎中的所有对象都使用控制反转来获取依赖关系。这些依赖项都实现了协议,并且从不直接在项目中访问,除了在引导阶段之外。为了得到这些对象,我有了服​​务定位器的概念。服务定位器的工作是定位符合特定协议的对象并将其返回。它很像一个工厂,但它也应该处理依赖关系。

为了向服务定位器提供服务,我有我所说的服务说明符。服务定位器知道项目中的所有服务说明符,当请求一个对象时,它会尝试从每个对象中获取符合所提供协议的对象实例。然后将该对象返回给调用者。这个设置很酷的是服务说明符也知道服务定位器,所以如果它有任何依赖关系,它只会向服务定位器询问那些特定的依赖关系。

举个例子,我有一个名为 HighScoreManager 的对象。 HighScoreManager 实现了 PHighScoreManager 协议。在任何时候,如果需要 PHighScoreManager 的实例,可以通过调用来检索它:

id<PHighScoreManager> highScoreManager = [ServiceLocator resolve: @protocol(PHighScoreManager)];

因此,控制反转。然而,大多数时候甚至没有必要这样做,因为大多数类都位于服务说明符中,如果需要 PHighScoreManager 作为依赖项,则通过服务定位器检索它。因此,我有一个很好的平面控制反转方法。

我的问题

因为我希望共享游戏引擎中的代码,所以我将其编译为静态库。这对其他一切都非常有用,但服务定位器似乎有点棘手。问题是一些服务会随着游戏的变化而变化。在我上面的例子中,一场比赛中的得分可能是时间,而在另一场比赛中可能是积分。因此,HighScoreManager 依赖于 PHighScoreCreator 的一个实例,它告诉它如何创建一个 PScore 对象。

为了向 HighScoreManager 提供 PHighScoreCreator,我需要为我的游戏提供服务说明符。我能想到的唯一方法是使用 Cocoa 版本的反射。在挖掘之后,我发现可以通过 NSBundle 发现类,但似乎没有办法获得当前的包。因此,如果我希望能够搜索我的服务说明符,我必须将我的游戏逻辑编译到它自己的包中,然后让引擎搜索这个包并加载它。为了做到这一点,我必须创建第三个项目来容纳引擎代码和游戏逻辑包,而实际上我只想拥有一个使用引擎静态库的游戏项目。

我真正的问题

毕竟,我的问题是

  1. 有没有更好的方法来完成我在 Cocoa Touch 中尝试完成的工作,或者
  2. 有没有办法从主包中发现符合我的服务说明符协议的类?

感谢您的帮助并花时间阅读问题。

-螺旋形

【问题讨论】:

    标签: objective-c cocoa inversion-of-control nsbundle service-locator


    【解决方案1】:

    看看:

    • +[NSBundle mainBundle];
    • +[NSBundle bundleForClass:];
    • +[NSBundle bundleWithIdentifier:];
    • +[NSBundle allBundles];
    • +[NSBundle allFrameworks];

    这些允许您在运行时以编程方式与各种捆绑包进行交互。一旦你有一个捆绑包可以使用,你可以使用许多策略来找到你正在寻找的特定类。例如:

    1. 检索包标识符 - 这将是一个类似于 @"com.example.GameEngineClient" 的 NSString。
    2. 通过剥离最后一个点之前的所有内容,或用下划线或其他方式替换所有点,然后附加预定义的协议名称,将其转换为合法的 Objective-C 类名称。例如,您的上述协议可能会生成类似 @"GameEngineClient_PHighScoreManager" 的字符串。
    3. 使用 NSClassFromString() 为您的协议获取包的指定类。

    现在您可以创建包作者提供的类的实例,该实例实现您指定的任何协议。

    Objective-C 运行时是一个美丽的东西!

    【讨论】:

    • 我尝试走这条路,但问题是您无法提前知道捆绑包中将包含哪些类名。因此,您不能遍历包中的所有类。
    • 我已经编辑了答案,以提供一些关于在您拥有 NSBundle 实例后下一步该做什么的建议。希望这可以帮助。 :-)
    • 我感觉这在 iPhone 应用程序中可能是不允许的,但我可能错了。我很确定苹果不希望你在 iPhone 上运行时加载类。但很好的问题。
    • Ron:这种模式不是关于在运行时加载类,而是关于定位它们。上面的代码假定所有这些类都在已经链接到您的应用程序的框架中。是的,这种模式对于动态加载的代码也很有用,但我认为 helixed 正在寻找的是减少使用他的游戏引擎所需的样板初始化代码的数量。
    • 在运行时动态创建类并没有错。您无法将新代码加载到您的应用程序中,这违反了开发协议。这就是为什么 iOS SDK 中缺少几个用于加载代码的捆绑方法的原因。
    【解决方案2】:

    听起来您需要使用Objective-C runtime 的功能。首先,您可以通过objc_getClassList 获取所有可用课程的列表。然后您可以遍历所有类并使用class_conformsToProtocol 检查它们是否符合您的协议。您不应该在此处使用 +conformsToProtocol: 消息,因为运行时中有不支持此选择器的类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多