Xcode更改未修改的故事板和XIB文件

139

故事板从git工作流的角度来看,当多个人共同协作时,可能会带来一些麻烦。例如,.storyboard文件中的XML具有其起始<document>标签的 toolsVersionsystemVersion属性,这些属性会随着最近的文件操作者所运行的配置而被改变。精确同步每个人的Xcode版本似乎可以帮助解决toolsVersion的问题,但是systemVersion则无论如何都会发生改变,这取决于开发者正在运行的具体Mac和/或OS X版本。

这很愚蠢,但大多数情况下是无害的。令我们担忧的是,在其他时间,通过git pull后打开Storyboards时会自动对Storyboard进行一些其他更改。也就是说,Alice修改了Storyboard,提交并将其推送到存储库。然后Bob拉取了Alice的更改,并打开Storyboard进行进一步的修改。他打开Storyboard的那一刻,文件图标立即变成了已修改但未保存状态,而且git status显示出现了许多奇怪的更改。所有这些在Bob没有进行任何更改或保存文件的情况下就发生了。

我们最常见的自动更改是在storyboard文件末尾消失或重新出现整个<classes>标签层次结构。我们还没有弄清楚是什么原因导致了这种情况。我们可能有几个本地化版本的storyboard存储在各种.lproj目录中,当在Interface Builder中打开它们时,类层次结构可能会在某些版本中自动被移除并添加到其他版本,或者在某些版本中保持不变。这会在git diff中产生很多噪音,但实际上并不会破坏任何功能。我们经常会选择性地将我们所做的实际更改添加到git的索引中,提交这些更改,然后丢弃这些自发的、毫无意义的<classes>更改。这样做是为了保持提交小而精致,就像它们应该的那样。然而,最终这变得太麻烦了,因为Xcode一直在重新进行更改,有人就一气之下提交了这些更改以及其他一些东西……这没问题,直到其他人的Xcode因为没有明显的原因想要将它们改回去。(我们的提交历史记录中有很多关于这个问题的诅咒。)
还有其他人看到这种行为吗?这是Xcode的一个bug还是我们某个或多个开发者Mac上的配置问题?我们在与XIB文件协作时也看到了一些类似的行为,但是storyboard似乎更容易出现这种情况。

实际上,Xcode项目和Git并不很兼容。我认为你无法避免这种混乱,除非放弃那些不必要的更改——对于我来说,几乎总是项目文件的更改以及其他XML文件的更改。如果有任何“解决方案”,我会很高兴。我喜欢Perforce的便捷锁定功能,它不允许Xcode进行太多更改,这可能需要手动完成,但只需查看而不更改文件。 - A-Live
3
不值得使用Storyboard与Git或其他任何东西。它们并不是为了方便提交而设计的。我们放弃了,改用.xib,虽然它也不是很好,但起码更细粒度。 - ahwulf
我们实际上发现故事板在许多方面都非常方便,尽管通常需要将它们与XIB混合使用。如果这个bug修复了,我们将非常乐意大部分时间都使用它们。 - JK Laiho
我必须评论一下ahwulf的评论:你到底是什么意思,说它们不友好?它们是XML /文本文件,这是你能得到的最友好的提交方式。我的storyboard和版本控制系统没有任何问题,唯一的问题当然是xcode有时候会删除<classes>标签,然后稍后重新添加它,但是如果您使用git GUI或git -p或其他dvcs等查看更改,您可以轻松地看到这一点。据我所知,我从未遇到过.pbxproj文件出现这种情况。 - mgrandi
我不明白为什么Xcode要把类块放在Storyboard里,如果它可以通过读取类文件来生成这些块,那么它们是一种“缓存”吗?如果是这样的话,它们应该被放在一个classes.cache文件中,这样我们就可以将其排除在版本控制之外... - hariseldon78
我对这个讨论没有任何贡献,除了表达我对这种情况的绝望。 - Kayce Basques
6个回答

84
这不是一个 Bug,而是 Xcode 处理 storyboard 文件的结果。我正在为 storyboard 文件编写一个差异和合并程序(GitHub 链接),花了很多时间分析 storyboard 文件的逻辑以及 Xcode 如何处理它。以下是我的发现:
  • 为什么 storyboard 文件会出现奇怪的更改? Xcode 使用 NSXML API 将 storyboard 文件解析成一些基于NSSet的逻辑树结构。当 Xcode 需要进行更改时,它会基于逻辑树结构创建一个 NSXMLDocument,清除 storyboard 文件,并调用 XMLDataWithOptions: 来重新填充文件。由于集合不保留其元素的顺序,即使是最微小的修改也可能改变整个 storyboard XML 文件。

  • 为什么 class 标签会随机消失或重新出现? <class> 部分只不过是一个内部 Xcode 缓存。Xcode 用它来缓存有关类的信息。缓存经常变化。当打开类的 .h/.m 文件时,会添加元素;而当 Xcode 检测到这些文件过时时(至少较旧版本的 Xcode 是这样做的),就会删除这些元素。当你保存 storyboard 时,会转储缓存的 当前版本,这就是为什么<class> 部分经常改变甚至消失的原因。

我没有对 Xcode 进行逆向工程;我通过使用 Xcode 和 storyboard 文件进行实验得出了这些观察结果。尽管如此,我几乎可以百分之百地确定它是这样工作的。

结论:

  • 缓存部分不重要;你可以安全地忽略其中的任何更改。
  • 与所有论坛上可以找到的相反,合并 storyboard 文件并不是一项复杂的任务。例如,假设您在 storyboard 文档中更改了名为 MyController1 的视图控制器。打开 storyboard 文件,找到类似于以下内容<viewController id=”ory-XY-OBM” sceneMemberID=”MyController1”>是一个视图控制器的标签,它的id属性为“ory-XY-OBM”,sceneMemberID属性为“MyController1”。如果您更改了segue或constraint,请提交任何包含“ory-XY-OBM”的内容。简单易懂!

9
Xcode的差异/合并工具有任何更新吗?听起来它将对这个社区非常有用。 - Tony
1
您可以从我的网页(请查看个人资料)获取该应用程序。该应用程序是100%免费的。 - Marcin Olawski
1
对我来说,尽可能拆分故事板 == 使用XIBs。 如果你想要将所有内容切片,使用XIBs会更容易和更好。 - Marcin Olawski
7
我没有对Xcode进行反向工程;我是通过实验Xcode和Storyboard文件得出这些观察结果的。这其实就是(浅层)的反向工程,而且很酷! :-) - Constantino Tsarouhas
19
这不是一个bug,而是Xcode处理Storyboard文件的后果。 尊重地说,这句话的后半部分并没有解释或合理化前半部分。我会改为:“这是一个bug。这是XCode处理.xib文件时不幸未解决且非常恼人的后果。”从XCode 5.x到6.2(截至150311),在IB中仅查看而未被开发人员修改的.xib文件会遭受毫无意义的XML更改。 这是一个影响生产力的严重问题。 像“没事,只需在git中处理块”之类的回答让我无言以对。 - cweekly
显示剩余8条评论

19

1
在4.6版本中也是同样的情况。也许这不是一个错误。 - Thromordyn
1
这不是一个漏洞,而是一种特性。:-S - MrTJ
没有证据表明这是一个错误 - 可能只是Xcode处理事情的方式,虽然有点烦人。 - Thomas Watson
重新排序和“类”部分很烦人,我完全支持为这些问题提交错误报告,无论苹果的工程师是否同意。然而,存储编辑文件的OS X版本是疯狂的,绝对是一个错误。 (我可以理解Xcode版本的相关性。但是,OS版本与此无关。) - Phil Calvin
1
7.0.1 没有带来任何变化。 - Andris Zalitis
显示剩余4条评论

11

这个问题可以通过非常谨慎地使用git add -p来缓解,包括storyboards、XIBs、Core Data模型和项目文件在内的所有Xcode生成的文件都会受到类似的瞬态修改的影响,这些修改对实际界面/模型/项目没有任何影响。

我在storyboards中看到的最常见的无用更改是系统版本号(如你所提到的)和<classes>部分的不断添加和删除,即使省略它也从未见过引起问题。对于XIBs,它是添加和删除<reference key="NSWindow"/>,但这甚至不是Cocoa Touch中的一个类,太神奇了。

把它想象成海:有高潮和低潮。让它冲过你。

啊。就是这样。

当暂存更改时,您可以忽略这些修改,重置无用更改并进行干净的提交。

从技术角度看,我发现storyboards比XIBs唯一的优点是Apple尚未阉割FileMerge以拒绝合并冲突的storyboards。(FileMerge曾经能够合并XIBs,但新版本破坏了它。谢谢你们!)

请在 http://bugreporter.apple.com/ 提交大量与所有这些问题有关的错误报告!别忘了在OpenRadar上创建条目。


6
在这里提供另一个答案,因为情况已经大大改善。代表StoryBoard的XIB文件的XML已经大大简化。
我最近开始使用Xcode中的界面源代码控制。我已经在命令行上工作多年并且感到很高兴,但是界面很好,它可以让你分割提交,如果你使用链接到提交的票务系统,这非常重要。
无论如何,我今天注意到Storyboard有一个更改,内置的差异显示了文档标记中的单个属性(systemVersion)。所以没什么大不了的。
我读过一些文章,人们说SB因合并问题而被禁用。完全疯了。他们如此惊人,特别是现在他们已经内置了智能自动布局,如果您不使用它们,您将会错过很多东西。

3

了解这种疯狂现象发生的原因很有帮助,但对于那些希望将其项目保持无警告并想要快速恢复项目健康状态的人:

  1. 在明确指示之前不要提交任何内容。

  2. 打开Xcode并创建一个新的storyboard(Command+N > iOS > User Interface > Storyboard)。我假设你使用默认名称Storyboard.storyboard

  3. 打开Xcode违反的storyboard。我假设这是Base.lproj/Main.storyboard

  4. 选择并复制storyboard上的所有内容(Command+A然后Command+C)。

  5. 打开Storyboard.storyboard

  6. 将所有内容复制并粘贴到Storyboard.storyboard中。

  7. 关闭Xcode。

  8. 打开终端并更改目录到您的存储库。

  9. mv Storyboard.storyboard Base.lproj/Main.storyboard替换Main.storyboardStoryboard.storyboard

  10. git add Base.lproj/Main.storyboard; git commit -m“修复Xcode的疯狂行为。”

  11. 通过git checkout -- project.pbxproj忽略对project.pbxproj的更改。如果您git diff文件,您将看到它只是添加了关于我们临时storyboard(现已不存在)的信息。

  12. 重新打开Xcode并查看警告是否消失。

  13. 深呼吸。


0

在同一故事板上工作并不是问题。但在同一视图控制器上工作会在拉取/合并时产生冲突,这是令人害怕的。对于一个大型团队来说,真的避免不了在同一视图控制器上工作。

好消息是,大多数情况下,如果我们理解xml结构,我们可以修复同一视图控制器的冲突。在团队中工作时,我从未失败过合并这些代码。假设您正在与一个视图控制器一起工作。您的视图当前为空白,请从源代码选项中查看视图控制器的xml结构。

enter image description here

Storyboard 是由文档类型标签绑定的 XML。Storyboard 中的所有内容都包含在 scene sceneID= 标签中。 scene 标签包含每个视图控制器。这是基础。

现在我们在视图上添加了一个 UILabel 和一个 UIButton。还设置了元素的自动布局。现在它看起来像:

enter image description here

在viewcontroller中添加一个级别/按钮,需要在视图的subview标记内添加一些新代码。对于更多元素添加或任何UI更改,同样适用。仔细检查标记结构非常重要,以解决任何冲突。

现在我们在storyboard中添加另一个名为Homeviewcontroller的viewcontroller。添加一个新的viewcontroller意味着在scenes标记下添加一个新场景。看看这个:

enter image description here

此时,我们将随机更改结构并观察问题/警告。我们首先更改viewcontroller标签的结束标记并保存文件。现在运行它并查看警告。错误说结束标记不正确,这是从第23行创建的。在第23行,我们看到一个标签约束被设置为没有结束标记。这就是问题所在。现在我们放置结束标记并构建项目。设置结束标记后,我们可以成功查看storyboard。

enter image description here

当遇到任何冲突警告时,请与您之前的源代码和更改的源代码进行比较。我们会删除旧的/冗余代码,保留新的代码,并使用适当的标签开始-结束来解决问题。

[注意,我将在有时间时更新答案并添加一些测试用例]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接