Xcode中的版本与构建有什么区别?

705

我有一个应用程序,是在Xcode 3中开发的,并最近开始在Xcode 4中进行编辑。在目标概要中,我有iOS应用程序目标形式的字段:标识符、版本、构建、设备和部署目标。版本字段为空,构建字段为3.4.0(与我仍在使用Xcode 3进行编辑时应用程序的版本相匹配)。

我的问题是:

  1. 版本字段和构建字段之间有什么区别?

  2. 为什么我升级到Xcode 4后版本字段为空?


首先,我认为它是显示在Xcode组织器存档列表中的构建号。除此之外,我不确定它还有什么用处。 - Daniel Dickison
8个回答

1280

苹果公司重新排列/重定义了这些字段。

今后,如果您在应用程序目标的“信息”选项卡上查看,您应该将“Bundle versions string, short”用作您的版本(例如3.4.0),并将“Bundle version”用作您的构建版本(例如500或1A500)。如果您没有看到它们两个,可以添加它们。它们将映射到“摘要”选项卡上的适当版本和构建文本框;它们是相同的值。

在查看“信息”选项卡时,如果您右键单击并选择显示原始键/值,您将看到实际名称是CFBundleShortVersionString(版本)和CFBundleVersion(构建)。

通常情况下,版本通常与Xcode 3一样使用。我不确定您关于版本/构建差异的问题提出了什么水平,因此我将从哲学的角度回答。

有各种方案,但流行的方案之一是:

{MajorVersion}.{MinorVersion}.{Revision}

  • 主要版本 - 主要更改,重新设计和功能更改
  • 次要版本 - 轻微的改进,新增功能
  • 修订 - 用于漏洞修补的补丁号

然后,构建单独用于指示发布或整个产品生命周期的总构建数。

许多开发人员从0开始构建编号,并且每次构建时将数字增加1,不断增加。在我的项目中,我有一个脚本,可以在每次构建时自动增加版本号。请参见下面的说明。

  • 发布1.0.0可能是构建542。需要542次构建才能发布1.0.0。
  • 发布1.0.1可能是构建578。
  • 发布1.1.0可能是构建694。
  • 发布2.0.0可能是构建949。

其他开发人员(包括苹果)具有由主要版本+次要版本+发布的构建数量组成的构建号。这些是实际的软件版本号,而不是用于营销的值。

如果您转到Xcode菜单 > 关于Xcode,您将看到版本和构建号码。如果您点击更多信息…按钮,则会查看不同版本的多个版本。自Xcode 5起,由于删除了更多信息…按钮,因此此信息也可以从系统信息应用程序的软件 > 开发人员部分中获得,可通过打开Apple菜单 > 关于此Mac > 系统报告…获得。

例如,Xcode 4.2(4C139)。营销版本4.2的Build主要版本为4,Build次要版本为C,Build编号为139。下一个版本(可能是4.3)将很可能是Build发布4D,并且Build编号将从0开始递增。

iPhone模拟器版本/Build号也是如此,iPhone、Mac等也是如此。

  • 3.2:(7W367a)
  • 4.0:(8A400)
  • 4.1:(8B117)
  • 4.2:(8C134)
  • 4.3:(8H7)

更新:按照需求,以下是创建脚本的步骤,该脚本在每次在Xcode中构建应用程序时运行,可以读取Build编号并将其递增,然后将其写回应用程序的{App}-Info.plist文件。如果您想将您的版本/Build号写入您的Settings.bundle/Root*.plist文件中,则可以执行可选的其他步骤。

这是从此处的教程文章中扩展来的。

在Xcode 4.2-5.0中:

  1. 加载您的Xcode项目。
  2. 在左侧面板中,单击项目的最顶部。这将加载项目设置编辑器。
  3. 在中心窗格的左侧,单击TARGETS下的您的应用程序。您需要为每个项目目标配置此设置。
  4. 选择Build Phases选项卡。
    • 在Xcode 4中,单击右下角的Add Build Phase按钮,然后选择Add Run Script
    • 在Xcode 5中,选择Editor菜单 > Add Build Phase > Add Run Script Build Phase
  5. 拖放新的Run Script阶段,将其移到Copy Bundle Resources阶段之前(当app-info.plist文件将与您的应用程序捆绑在一起时)。
  6. 在新的Run Script阶段中,设置Shell/bin/bash
  7. 将以下内容复制并粘贴到整数构建编号的脚本区域中:

    buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
    buildNumber=$(($buildNumber + 1))
    /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"
    

    如@Bdebeez所指出的,苹果通用版本控制工具(agvtool)也可用。如果您更喜欢使用它,则需要先执行以下几个步骤:

    • 选择生成设置选项卡。
    • 版本控制部分下,将 当前项目版本 设置为您想要使用的初始构建版本号,例如 1
    • 回到生成阶段选项卡,将您的运行脚本阶段拖放到复制包资源阶段之后,以避免在尝试同时构建和更新包含构建号的源文件时发生竞态条件。

    请注意,使用agvtool方法仍然可能会定期出现失败/取消的构建,并且没有错误。出于这个原因,我不建议使用此脚本与agvtool一起使用。

    尽管如此,在您的运行脚本阶段,您可以使用以下脚本:

    "${DEVELOPER_BIN_DIR}/agvtool" next-version -all
    
    next-version 参数增加构建号(bump 是同样的别名),-all 更新 Info.plist 中的新构建号。

  8. 如果您有一个显示版本和构建的设置包,则可以在脚本末尾添加以下内容以更新版本和构建。注意:更改 PreferenceSpecifiers 值以匹配您的设置。 PreferenceSpecifiers:2 表示查看 plist 文件中 PreferenceSpecifiers 数组下标为 2 的项目,因此对于基于 0 的索引,这是数组中第三个首选项设置。

    productVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$INFOPLIST_FILE")
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root.plist
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root.plist
    

    如果您使用agvtool而不是直接读取Info.plist,则可以将以下内容添加到脚本中:

    buildNumber=$("${DEVELOPER_BIN_DIR}/agvtool" what-version -terse)
    productVersion=$("${DEVELOPER_BIN_DIR}/agvtool" what-marketing-version -terse1)
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root.plist
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root.plist
    
    如果您拥有适用于iPad和iPhone的通用应用程序,那么您也可以为iPhone文件设置设置:
  9. /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $buildNumber" Settings.bundle/Root~iphone.plist    
    /usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $productVersion" Settings.bundle/Root~iphone.plist
    

18
在我的项目中,我有一个脚本,每次构建时会自动增加版本号。你能分享一下如何做到这一点吗?感谢您提供详细的答案和原始问题。 - Zsolt
9
иҰҒеўһеҠ еҚҒе…ӯиҝӣеҲ¶ж•°пјҢжӮЁеҸҜд»ҘдҪҝз”Ёд»ҘдёӢд»Јз Ғпјҡ buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE") dec=$((0x$buildNumber)) buildNumber=$(($dec + 1)) hex=$(printf "%X" $buildNumber) /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $hex" "$INFOPLIST_FILE"иҜ·жіЁж„ҸпјҢжӯӨд»Јз Ғе°ҶиҺ·еҸ–дёҖдёӘеҚҒе…ӯиҝӣеҲ¶ж•°пјҢе°Ҷе…¶иҪ¬жҚўдёәеҚҒиҝӣеҲ¶ж•°еӯ—пјҢйҖ’еўһдёҖпјҢ并е°Ҷз»“жһңиҪ¬жҚўеӣһеҚҒе…ӯиҝӣеҲ¶гҖӮжңҖеҗҺпјҢе®ғе°ҶдҪҝз”Ё PlistBuddy е‘Ҫд»Өжӣҙж–° INFOPLIST_FILE дёӯзҡ„ CFBundleVersionгҖӮ - Alon Amir
8
简而言之:AppStore 不允许使用 HEX。 - Nicolas Miari
3
(适用于Xcode 5用户)您可能需要更改第5步的内容,将其更改为:“从菜单栏中选择编辑器->添加构建阶段->添加运行脚本构建阶段”。 - Greg M. Krsak
2
提示:如果您勾选了“仅在安装时运行脚本”选项,则只有在生成存档时才会增加buildVersion。 - Bron Davies
显示剩余12条评论

74

(我只是为了自己的参考而留下这个。)这将显示Xcode目标中您看到的“版本”和“构建”字段的版本和构建:

- (NSString*) version {
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    return [NSString stringWithFormat:@"%@ build %@", version, build];
}

在Swift中

func version() -> String {
    let dictionary = NSBundle.mainBundle().infoDictionary!
    let version = dictionary["CFBundleShortVersionString"] as? String
    let build = dictionary["CFBundleVersion"] as? String
    return "\(version) build \(build)"
}

2
OT: 你的方法有泄漏 - 你使用了 alloc/init 来初始化字符串,这将导致字符串被保留,但是你没有释放它。对于从方法返回的对象,通常应该使用一个 convenience 方法来让字符串自动释放,或者调用 autorelease。可以选择以下两种方式中的一种:return [NSString stringWithFormat:@"%@ build %@", version, build]; 或者 return [[[NSString alloc] initWithFormat:@"%@ build %@", version, build] autorelease]; - nekno
1
感谢@nekno,已更改答案以使其支持ARC或非ARC。 - Dan Rosenstark
2
最好在可用的情况下使用常量(例如kCFBundleVersionKey),以避免拼写错误。尽管我找不到“CFBundleShortVersionString”的常量 :) - DannyA
你的 Swift 代码有一个错误 - 你调用了 CFBundleShortVersionString 两次。 - Yariv Nissim
谢谢@yar1vn,我已经修复了,而且它不是反向的。 - Dan Rosenstark

59

Build number是一个内部编号,表示应用程序的当前状态。 它与版本号不同,通常不面向用户,并且不表示任何差异/功能/升级,就像版本号通常会做的那样。

可以这样想:

  • Build(CFBundleVersion):构建的数量。 通常情况下,您从1开始,并随着每个应用程序的构建而增加1。 它快速允许比较哪个版本更近,并表示代码库的进展感。在与QA一起工作并需要确保错误日志记录在正确的构建中时,这些可能非常有价值。
  • Marketing Version(CFBundleShortVersionString):您正在使用的面向用户的数字,以表示应用程序的此版本。 通常,这遵循Major.minor版本方案(例如MyAwesomeApp 1.2),以让用户知道哪些发布是较小的维护更新,哪些是重大的新功能。

为了在项目中有效地使用它,苹果提供了一个很棒的工具,称为agvtool我强烈建议使用它,因为它比编写plist更改要简单得多。 它允许您轻松设置构建号和营销版本。 当脚本化(例如,在每个构建中轻松更新构建号,甚至查询当前构建号是什么)时,它特别有用。 它甚至可以做更多奇异的事情,比如在更新生成号时为您标记SVN。

使用方法:

  • 在Xcode中将您的项目设置为使用“Apple Generic”。
  • 在终端中
    • agvtool new-version 1(将生成号设置为1)
    • agvtool new-marketing-version 1.0(将营销版本设置为1.0)

请参见agvtool的man页面以获取大量有用信息


另一篇关于 agvtool 的编程文章:使用 agvtool 轻松为 iPhone 应用程序进行版本管理 - Gon

25

我从stackoverflow获取了一个自动增加构建号的脚本,但是如果构建号是浮点数,那么上面答案中的脚本就没有用了,因此我进行了一些修改:

#!/bin/bash    
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=`echo $buildNumber +1|bc`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

23

营销发布号码是针对客户的,称为版本号。它以1.0开始,并在主要更新时增加到2.03.0,在次要更新时增加到1.11.2,在修复错误时增加到1.0.11.0.2。此编号围绕发行版和新功能。

构建号码大多是迄今为止制作的内部构建号码。但有些人使用其他数字,如存储库的分支号码。该数字应该是唯一的,以区分不同的几乎相同的构建版本。

正如您所看到的,构建号码并不是必需的,您可以选择使用哪个构建号码。因此,如果您将Xcode更新到主要版本,则构建字段为空。但版本字段可能不为空!


要将build号码作为NSString变量获取:

NSString * appBuildString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];

要将版本号作为NSString变量获取:

NSString * appVersionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];

如果你想在一个NSString中同时获取两者

NSString * versionBuildString = [NSString stringWithFormat:@"Version: %@ (%@)", appVersionString, appBuildString];

这是使用 Xcode版本4.6.3(4H1503) 进行的测试。构建号通常用括号/花括号括起来。构建号可以是十六进制或十进制。

buildandversion


Xcode 中,您可以通过在项目设置中的 Run script 构建阶段中添加以下内容来自动递增 十进制的构建号

#!/bin/bash    
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

使用此脚本来生成十六进制版本号

buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
buildNumber=$((0x$buildNumber)) 
buildNumber=$(($buildNumber + 1)) 
buildNumber=$(printf "%X" $buildNumber)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

project_settings


6

感谢 @nekno 和 @ale84 提供了很好的答案。

然而,我稍微修改了 @ale84 的脚本,使它可以对浮点数增加构建号。

incl 的值可以根据您的浮点格式要求进行更改。 例如:如果 incl = .01,则输出格式将为 ... 1.19, 1.20, 1.21 ...

buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
incl=.01
buildNumber=`echo $buildNumber + $incl|bc`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

0
我正在使用 Xcode 14.2,并将以下 Bash 脚本添加到项目 bundle 中的 .sh 文件中。然后,我使用运行脚本构建阶段来调用此脚本。在归档项目时,版本和 bundle 更新了几次,但之后停止递增。请提供有关脚本为什么无法继续递增的任何反馈。感谢反馈。
currentVersionString=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$PROJECT_DIR/SupportingFiles/$INFOPLIST_FILE")
currentBuildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$PROJECT_DIR/SupportingFiles/$INFOPLIST_FILE")

newBuildNumber=$((currentBuildNumber + 1))

IFS='.' read -ra version <<< "$currentVersionString"
version[2]=$((version[2] + 1))
newVersionString="${version[0]}.${version[1]}.${version[2]}"

/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $newVersionString" "$PROJECT_DIR/SupportingFiles/$INFOPLIST_FILE"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $newBuildNumber" "$PROJECT_DIR/SupportingFiles/$INFOPLIST_FILE"

如果您有新的问题,请通过单击提问按钮来提出。如果它有助于提供上下文,请包含此问题的链接。- 来自审核 - storoj

0
另一种方法是在`appDelegate`的`didFinishLaunchingWithOptions`中设置版本号。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     NSString * ver = [self myVersion];
     NSLog(@"version: %@",ver);

     NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
     [userDefaults setObject:ver forKey:@"version"];
     return YES;
}

- (NSString *) myVersion {
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    return [NSString stringWithFormat:@"%@ build %@", version, build];
}

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