如何在OS X或iOS(不使用Gestalt)的运行时确定OS版本?


72

CarbonCore/OSUtils.hOS X 10.8 Mountain Lion开始不推荐使用位于其中的Gestalt()函数。

我经常使用此功能在运行时测试OS X操作系统的版本(请参见下面的玩具示例)。

在Cocoa应用程序中,在运行时还可以使用什么其他API来检查OS X操作系统版本?


1
有关于这个问题,同样的事情进行一些讨论:stackoverflow.com/questions/11055146/...
Monolo

格式塔API非常旧(实际上是预碳),并且大多已弃用。尽管它们保持不变并且可以正常工作,但是如今使用它们并不是一个好主意。甚至没有运行时版本……
Motti Shneor '16

Answers:


69

在OS X 10.10(和iOS 8.0)上,您可以使用[[NSProcessInfo processInfo] operatingSystemVersion],它返回一个NSOperatingSystemVersion结构,定义为

还有一种方法NSProcessInfo可以为您进行比较:

当心,虽然记载了OS X 10.10及更高版本可用,都operatingSystemVersionisOperatingSystemAtLeastVersion:在OS X 10.9(存在可能10.9.2如预期)和工作。这意味着您不得测试是否NSProcessInfo响应这些选择器以检查您是否在OS X 10.9或10.10上运行。

在iOS上,仅从iOS 8.0开始有效使用这些方法。


2
您还可以使用以下Objective-C类别(已获得BSD许可)在较旧的操作系统版本上使用这些新方法:github.com/petroules/CocoaBackports/blob/master/CocoaBackports/…它应该可以在OS X 10.5和iOS上运行2.0与MRC,ARC或GC以及较旧的SDK。
Jake Petroules 2014年

请注意,如果您在iOS模拟器中运行应用程序,它将返回OS X的版本。
Andreas Ley 2014年

从iOS 8 beta 4开始,该operatingSystemVersion方法在iOS模拟器中运行时会返回正确的版本。
0xced 2014年

1
如何从C ++代码调用[[NSProcessInfo processInfo] operatingSystemVersion]?
弗拉德

2
如果我已经知道我使用的是10.10,则无需检查!😂------在OS X 10.10上
David

27

在命令行上:

以编程方式:

达尔文版本到OS X发行版:

获取和测试版本的样本:

我的机器上的结果macOS High Sierra 10.13.2


1
请注意,这些结果与您获得的结果相同uname(在卡尔·诺鲁姆的答案中进行了讨论)。它们是内核版本号,在某种程度上与OS版本相对应,但不是很明显。
罗布·纳皮尔

4
切勿使用它来确定OS X或iOS版本。虽然您现在可以从算术上确定OS X的另一个版本,但不能保证将来可以进行这种比较。举一个具体的例子,一个iOS版本已经跳过了Darwin版本。同样可以与OS X的发生
杰克Petroules


19

有一个可可API。您可以从类NSProcessInfo获取os X版本字符串。

获取操作系统版本字符串的代码如下。

// === >>版本10.8.2(内部版本12C2034)结果值

被弃用。


10.10添加了一种以结构化方式(从字面上看)获取数据的方法,但是只有在以10.10 SDK为目标时才可以使用它。目前,这不符合目的。您现在仍然可以使用Gestalt(),但已过时,它可能随时消失。
西沃恩

这可能很棒。它没有过时,在10.2+ SDK作品......不幸的是,苹果的文件说The operating system version string is human readable, localized, and is appropriate for displaying to the user. This string is not appropriate for parsing. developer.apple.com/reference/foundation/nsprocessinfo/...
BuvinJ

1
@Vikas Bansal的答案可以说是更好的。
BuvinJ

12

还有kCFCoreFoundationVersionNumber,如果您只需要检查要支持的最低版本,则可以使用它。这样做的好处是它可以追溯到10.1,并且可以用C,C ++和Objective-C完成。

例如,要检查10.10或更高版本:

您将需要与CoreFoundation(或Foundation)框架链接。

它在Swift中的工作方式也完全相同。这是另一个例子:


谢谢,这很好,也正好是我在寻找的东西-不需要Appkit(链接到Obj-C代码和运行时),不需要系统调用,文件读取等等,由-维护的简单常量操作系统。但是,有一件事-应该包含哪个标头才能使该常数可用?
Motti Shneor '16

@MottiShneor我添加了必要的#include和链接信息。
kainjow '16

谢谢!现在很完美:)我认为有人(嗯...我?)应该总结所有选择,(对不起,我要说的不多),并说明哪种情况下最好。我必须说我个人最喜欢CoreFoundation。但是-如果您正在编写unix风格的守护程序,甚至没有链接CoreFoundation,那么您该怎么办?恢复到sysctl()吗?
Motti Shneor '16

@MottiShneor为什么不链接到OS X上的CF?易于使用的平台特定项目设置。即使使用C代码也可以与Foundation链接并使用Objective-C API,只需将其包装在C函数中即可。
kainjow,2016年

是否kCFCoreFoundationVersionNumber10_XXX为高于10.10.3的版本提供了这些常量?这是我找到的XCode 8.0的CFBase.h标头中的最大常量。
西蒙·沃塔

12

您可以使用以下方式轻松获取操作系统的主要,次要修补程序版本 NSOperatingSystemVersion


2
仅OS X 10.10和更高版本。
bleater

1
最好的答案是@bleater指出,直到10.10才可以使用。
BuvinJ

在构建时或在运行应用程序的计算机上是否需要OS X 10.10?
西蒙·沃塔

9

或者,更简单地说,下面是代码:

我希望这可以帮助别人。


1
谢谢你的评论。解决该问题的方法是什么,所以我可以在答案中包括?
neowinston 2014年

谢谢,这在一个奇怪的用例中很好用。为了确定是否支持HEVC / H265编解码器(理论上iOS 11支持,但仅当模拟器在macOS 10.13或更高版本上运行时),我需要在iOS 11模拟器上运行一些iOS测试用例时需要知道macOS的版本。后来)。
奥斯卡·耶罗

8

如果您有一个需要在10.10以及更低版本上运行的应用程序,请使用以下解决方案:

请注意,即使10.9似乎也响应了operatingSystemVersion选择器,因此我认为它只是10.9中的私有API(但仍然有效)。

这适用于OS X的所有版本,并且不依赖于字符串解析或文件I / O。


7
不要使用gestaltSystemVersion!如果任何版本组件大于9,这将失败。例如,OS X 10.4.11将返回为10.4.9。而是使用gestaltSystemVersionMajor,gestaltSystemVersionMinor和gestaltSystemVersionBugFix分别检索每个版本组件。
Jake Petroules 2014年

2
仅供参考,这仍然依赖于文件I / O内部...这两种方法都会读SystemVersion.plist每次被调用时(无缓存)。
Jake Petroules 2014年

1
由于某些原因,即使我在小牛队respondsToSelector上,进行检查也operatingSystemVersion返回“是”。
ZS

1
要使用OperatingSystemVersion,OS X SDK应该是什么版本?我正在使用Xcode 4.4.6,而SDK是OS X 10.8。我可以将此SDK与它一起使用,还是需要安装最新的Xcode
Akhil Shrivastav 2014年

7

这实际上是以上答案的汇总,并对需要的开发人员提供了进一步的指导。

OS-X通过多种方式在运行时提供其版本。每种方式都更适合特定的开发方案。我将尝试对它们全部进行总结,并希望如果我忘记了某些内容,其他人也可以完成我的回答。

首先,全面列出获取操作系统版本的方法。

  1. uname命令行工具和功能提供OS的UNIX(达尔文)版本。尽管这不是OS的营销版本,但是它与OS唯一匹配,因此您可以从中推导出OS-X营销版本。
  2. sysctl kern.osrelease 命令行(或 sysctlbyname("kern.osrelease", str, &size, NULL, 0)函数)将提供与uname相同的信息,在某种程度上更易于解析。
  3. Gestalt(gestaltSystemVersionMajor)(带有“ Minor”和BugFix“”变体是用于获得市场营销OS版本的最古老的(Carbon!)API,但仍然不推荐使用。
  4. NSAppKitVersionNumber是AppKit框架的浮点常量,它将提供OS-X Appkit版本(与OS版本一致),可用于所有链接到AppKit的应用程序。它还提供了所有可能版本的全面列举(例如NSAppKitVersionNumber10_7_2
  5. kCFCoreFoundationVersionNumber是一个CoreFoundation框架的浮点常量,与Appkit对应的常量相同,可用于与C,Obj-C和Swift中与CoreFoundation链接的所有应用程序。它还提供了所有OS X发行版的全面枚举(例如kCFCoreFoundationVersionNumber10_9
  6. [[NSProcessInfo processInfo] operatingSystemVersionString]; 是Obj-C中可用于OS-X和iOS应用程序的Cocoa API。
  7. 有一个资源.plist,/System/Library/CoreServices/SystemVersion.plist其中包括“ ProductVersion”键中的OS版本。NSProcessInfo从该文件中读取其信息,但是您可以使用所选的PList读取API直接执行此操作。

有关每个选项的更多详细信息,请查阅上面的答案。那里有很多信息!


1
5号不再更新。即使在10.13中,最新的定义也用于kCFCoreFoundationVersionNumber10_11_Max,因此在此找不到10.12和10.13的定义。
Mecki '18

现在,我最喜欢的#4也已停止更新-最新值为MacOS SDK 10.13上的NSAppKitVersionNumber10_12_2。耻辱。
Motti Shneor '18

6

uname(3)

uname()函数将以零结尾的信息字符串(标识当前系统)存储到由引用的结构中name

utsname结构在<sys/utsname.h>头文件中定义,并且包含以下成员:

  • sysname -操作系统实现的名称。
  • nodename -本机的网络名称。
  • release -操作系统的发行级别。
  • version -操作系统的版本级别。
  • machine -机器硬件平台。

2
这实际上不起作用,因为uname()返回的是内核版本,而不是市场版本(例如“ 10.8”。)
Jonathan Grynspan 2012年

1
是的,乔纳森是正确的。我尝试了此操作,并报告:release: "12.0.0", version: "Darwin Kernel Version 12.0.0: Thu Jun 7 18:47:37 PDT 2012; root:xnu-2050.6.71~1/RELEASE_X86_64"
Todd Ditchendorf 2012年

2
uname()对于获取移动设备模型(例如"iPad2,1")确实很有用,因此...并非完全没有用。:)
Jonathan Grynspan 2012年

1
内核版本和市场版本之间没有1:1映射吗?
卡尔·诺鲁姆

1
@JonathanGrynspan:主要是线性的。达尔文主要版本比OS X次要版本提前4个版本(例如10.4是达尔文8、10.6是达尔文10、10.8是达尔文12)。达尔文次要版本与格式塔“错误修复”相同。例如,我的系统正在运行10.8.2,而内核版本是12.2。在我的10.4.11系统上,内核版本为8.11。我的10.5.8盒子也是Darwin 9.8。
dreamlax

6

这是我用的:

建议在AppKit发行说明中

如果应用被沙箱化,则无法读取/System/Library/CoreServices/SystemVersion.plist


2
“如果应用被沙箱化,则无法读取/System/Library/CoreServices/SystemVersion.plist”-这是不正确的。根据Apple文档和我自己的经验,沙盒应用程序可以访问/ System中世界可读的项目。见developer.apple.com/library/mac/documentation/security/...
杰克Petroules

5

sysctl今天在玩macOS时,我偶然发现kern.osproductversion其中确实包含当前的OS版本。在运行Mojave的Mac上,我得到:

当然,也可以通过编程方式检索该值:

提交到xnu内核代码库表明osproductversion是2018年中的最新功能。我在10.14.3和10.13.6上成功进行了测试。

总而言之,我认为最好的程序化,非可可方法是检查是否kern.osproductversion产生有效结果,然后退回到该答案中kern.osrelease所述的手动映射。


3

Gestalt()是一个纯C API。可接受的答案建议使用,NSProcessInfo但这需要使用Objective-C代码,并且如果出于某种原因要求使用纯C,则不能使用。对于NSAppKitVersionNumber,情况也是如此,自10.13.4起,Apple也不再对其进行更新。kCFCoreFoundationVersionNumber在C代码中可以使用该常数,但是自10.11.4起,此常数就没有更新过。阅读kern.osrelease使用sysctl在C语言中可以,但需要使用映射表或依赖于当前的内核版本控制方案,因此对于将来的版本而言并不可靠,因为内核版本方案可能会更改,并且映射表无法知道将来的版本。

Apple的官方推荐方法是阅读,/System/Library/CoreServices/SystemVersion.plist因为这也是Gestalt()从中获取版本号的地方,这也是NSProcessInfo()从中获取版本号的地方,这也是命令行工具sw_vers从中获取版本号的地方,甚至是新的@available(macOS 10.x, ...)编译器功能从中获取版本号(我深入研究系统以了解相关信息)。如果连Swift#available(macOS 10.x, *)都从那里得到版本号,我也不会感到惊讶。

从那里读取版本号没有简单的C调用,因此我编写了以下纯C代码,它仅需要CoreFoundation本身就是纯C框架的框架:

使用adispatch_once_f代替的原因dispatch_once是,块也不是纯C的。完全使用dispatch_once该版本的原因是,该版本号在系统运行时无法更改,因此没有理由在单个应用程序运行期间多次读取该版本号。另外,不能保证读取它是故障安全的,并且每次应用程序需要读取它时都无法重新读取它。

谈论即使不是不可能的,也要保证它不会失败,但阅读它当然可能会失败,在这种情况下,该函数将返回false并且始终会返回false。我将由您自己决定以有意义的方式在您的应用程序代码中处理这种情况,因为我不知道知道版本号对您的应用程序有多重要。如果版本号不是很严格,则可以解决该问题,否则应以用户友好的方式使应用程序失败。

注意
如果您仅需要版本号来执行替代代码(具体取决于系统版本),则可以自行查询版本号,这是更好的选择。有关详细信息,请参见此问题


0

比赛晚了,但我最终还是在这里寻找答案。对于它的价值,也许它对其他人有用。

过去,我使用命令行方法:

结果是:

可以单独询问每一行(注意驼峰符号):

话虽如此,感谢这里列出的所有其他解决方案...


0

//我为Swift(Multiplatform)支付的两分钱


0

macOS Catalina及更高版本的更新

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.