在Xcode中组织大型Cocoa应用程序源代码的最佳方法是什么?

时间:2011-07-08 13:49:34

标签: objective-c xcode cocoa modularity

以下是我要找的内容:

我想将各种功能分成某些模块或组件,以限制其他类的可见性,以防止每个类都可以访问所有其他类,这会随着时间的推移导致意大利面条代码

在Java&例如,我会使用包并将每个包放入一个具有明确定义的依赖结构的单独项目中。

我考虑过的事情:

  1. 单独的文件夹用于源文件并使用Xcode中的组
    • 优点:操作简单,几乎不需要Xcode配置
    • 缺点:没有编译时功能分离,即访问所有内容只有一个#import声明
  2. 使用框架
    • 优点:框架代码无法访问框架之外的访问类。这强制封装并保持分离
    • 缺点:如果您同时处理多个框架,则代码管理很麻烦。每个框架都是一个单独的Xcode项目,带有一个单独的窗口
  3. 使用插件
    • 优点:与Frameworks类似,插件代码无法访问其他插件的代码。在编译时清除分离。插件源可以是同一个Xcode项目的一部分。
    • 缺点:不确定。这可能是要走的路......
  4. 根据您的经验,在能够编辑同一项目中的所有来源时,您会选择将内容分开?

    修改

    • 我的目标是Mac OS X
    • 我真的在寻找一种在编译时强制分离的解决方案
    • 通过插件我的意思是Cocoa包(http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/LoadingCode/Concepts/Plugins.html)

4 个答案:

答案 0 :(得分:9)

我参与了一些大小合适的Mac项目(在我的最后一个90 xcodeproj文件中使用了> 2M SLOC)以下是我对管理它们的想法:

  • 除非您实际在组之间共享二进制文件,否则请避免使用Frameworks,Bundles或dylib等动态加载。这些往往会产生比我在经验中解决的更多的复杂性。另外,它们不容易移植到iOS,这意味着要保持多种方法。最糟糕的是,拥有大量动态库会增加两次包含相同符号的可能性,从而导致各种疯狂的错误。当您直接在多个库中直接包含一些“helper”类时会发生这种情况。如果它包含一个全局变量,那么错误很棒,因为不同的线程使用不同的全局实例。

  • 静态库是许多(如果不是大多数情况下)的最佳选择。它们在构建时解决所有问题,允许在C / C ++中进行代码剥离以及动态库中不可能实现的其他优化。他们摆脱了“嘿,它加载到我的系统而不是客户的”(当你使用错误的值框架路径时)。从崩溃堆栈计算行号时无需处理幻灯片。它们在构建时捕获重复的符号,节省了数小时的调试痛苦。

  • 将主要组件分隔为单独的xcodeproj。但是,真的想想这里的“主要”意味着什么。我的90个项目产品太多了。只是进行依赖性检查可能会成为一项非常重要的工作。 (Xcode 4可以改进这个,但是在我们能够让Xcode 4可靠地构建它之前我离开了项目,所以我不知道它到底有多好。)

  • 将公共标题与私有标题分开。您可以使用静态库来完成此操作,就像使用Frameworks一样。将公共标头放在不同的目录中。为此,我建议每个组件都有自己的公共include目录。

  • 不要复制标题。直接从组件的公共include目录中包含它们。在您执行此操作之前,将标头复制到共享树中似乎是一个好主意。然后你会发现你正在编辑副本而不是真正的副本,或者你正在编辑真实副本,而不是实际复制副本。无论如何,它使开发成为一个令人头疼的问题。

  • Use xcconfig files, not the build pane.构建窗格会让您在这些大项目中疯狂。我倾向于有这样的行:


common="../../common"
foo="$(common)/foo"
HEADER_SEARCH_PATHS = $(inherited) $(foo)/include

  • 在公共标头路径中,包含您自己的捆绑名称。在上面的示例中,主标头的路径为common/foo/include/foo/foo.h。额外的水平看起来很痛苦,但是当你导入时它真的是一个胜利。然后,您始终像这样导入:#import <foo/foo.h>。保持一切都很干净。不要使用双引号导入公共标头。仅使用双引号在您自己的组件中导入私有标头。

  • 我还没有确定Xcode 4的最佳方法,但在Xcode 3中,您应该始终通过将项目添加为子项目并将“.a”目标拖动到链接步骤来链接您自己的静态库。这样做可确保您链接为当前平台和配置构建的链接。我真正庞大的项目还没有能够转换为Xcode 4,所以我对那里的最佳方式还没有强烈的意见。

  • 避免在链接步骤中搜索自定义库(-L-l标志)。如果您将库构建为项目的一部分,那么请使用上面的建议。如果您预先构建它,请在LD_FLAGS中添加完整路径。搜索库包括一些令人惊讶的算法,并使整个事情难以理解。切勿将预先构建的库放入链接步骤。如果您将预先构建的libssl.a放入链接步骤,它实际上会为路径添加-L参数,然后添加-lssl。在默认搜索规则下,即使您在构建窗格中显示libssl.a,您实际上也会链接到系统libssl.so。删除库将删除-l,但不会移除-L,这样您就可以找到奇怪的搜索路径。 (我讨厌构建窗格。)在xcconfig中这样做:


LD_FLAGS = "$(openssl)/lib/libssl.a"

  • 如果你有几个项目之间共享的稳定代码,并且在开发这些项目时你永远不会搞乱这些代码(并且不希望源代码可用),那么框架可以是一种合理的方法。如果您需要插件以避免加载大量不必要的代码(并且在大多数情况下您实际上不会加载该代码),那么捆绑可能是合理的。但在应用程序开发人员的大多数情况下,从静态库链接在一起的一个大型可执行文件是IMO的最佳方法。只有在运行时实际共享时,共享库和框架才有意义。

答案 1 :(得分:4)

我的建议是:

  1. 使用框架。它们是您列出的选项中最容易重复使用的构建工件,并且您描述要尝试实现的结构的方式听起来非常类似于创建一组框架。

  2. 为每个框架使用单独的项目。如果将所有内容转储到单个项目中,您将永远无法让编译器强制执行您想要的访问限制。如果你不能让编译器强制执行它,那么祝你的开发人员好运。

  3. 升级到XCode4(如果还没有)。这将允许您在单个窗口中处理多个项目(非常类似于Eclipse的工作方式),而无需混合项目。这几乎消除了您在框架选项下列出的缺点。

  4. 如果您的目标是iOS,我强烈建议您构建real frameworks,而不是使用bundle-hack方法获得的伪造的,如果您尚未构建真正的框架。< / p>

答案 2 :(得分:2)

我已经设法通过强迫自己努力练习模型 - 视图 - 控制(MVC)以及健康的数量来保持我的理智,使我的项目在过去几个月中发展到相当大(类别数量)注释,以及不可或缺的源代码控制(颠覆,然后是git)。

一般来说,我会观察以下内容:

“模型”从NSObject继承的Objective-C 1类或从NSObject继承的自定义“模型”类序列化数据(从哪里开始,包括应用程序的'状态'无关)的类。我之所以选择Objective-C 1.0是为了兼容性,因为它是最低的共同点,而且由于Objective-C 2.0特性的依赖性,我不想在将来从头开始编写“模型”类。

View Classes在XIB中,XIB版本设置为支持我需要支持的最旧的工具链(因此除了Xcode 4之外,我还可以使用以前版本的Xode 3)。我倾向于从Apple提供的Cocoa Touch API和框架开始,以便随着这些API的发展,Apple可能会引入任何优化/增强功能。

控制器类包含管理视图的显示/动画(通过编程方式以及来自XIB)的通常代码以及来自“模型”类的数据的数据序列化。

如果我发现自己多次重复使用一个类,我会探索重构代码并优化(使用Instruments测量)到我称之为“实用程序”的类或协议。

希望这会有所帮助,祝你好运。

答案 3 :(得分:0)

这在很大程度上取决于您的情况和您自己的特定偏好。

如果您正在编写“正确的”面向对象的类,那么您将拥有一个类结构,其中必要时隐藏其他类的方法和变量。除非您的项目很庞大并且由数百个不同的可区分模块构建,否则它可能足以将类和资源分组到XCode中的文件夹/组中并以此方式使用它。

如果您真的有一个具有易于区分的模块的huuge项目,那么无论如何都要创建一个框架。我建议虽然只有在不同的应用程序中使用相同的代码才会真正有必要,在这种情况下,创建框架/额外项目将是在项目之间有效复制代码的好方法。在几乎所有其他情况下,它可能只是过度杀伤而且比需要的要复杂得多。

你的最后一个想法似乎是前两个的混合。插件(据我所知你正在描述 - 告诉我,如果我错了)只是在同一个项目中分开的类?这可能是最好的方法,在任何情况下都应该(在一定程度上)完成。如果要创建绘制图形的功能(例如),则应该关闭新文件夹/组并在其中启动类和功能,只在必要时将这些类包含在主应用程序中。

让我这样说吧。没有理由超越顶部...但是,即使只是为了您自己的理智 - 或代码的可维护性 - 您应该始终努力将所有内容分组到描述性组/文件夹中。