如何在OS X或iOS中运行OS版本(不使用Gestalt)?

Tod*_*orf 67 macos cocoa macos-carbon ios

CarbonCore/OSUtils.h自OS X 10.8 Mountain Lion 起,位于的Gestalt()函数已被弃用.

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

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

int main() {
    SInt32 versMaj, versMin, versBugFix;
    Gestalt(gestaltSystemVersionMajor, &versMaj);
    Gestalt(gestaltSystemVersionMinor, &versMin);
    Gestalt(gestaltSystemVersionBugFix, &versBugFix);

    printf("OS X Version: %d.%d.%d\n", versMaj, versMin, versBugFix);
}
Run Code Online (Sandbox Code Playgroud)

0xc*_*ced 64

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

typedef struct {
    NSInteger majorVersion;
    NSInteger minorVersion;
    NSInteger patchVersion;
} NSOperatingSystemVersion;
Run Code Online (Sandbox Code Playgroud)

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

- (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version
Run Code Online (Sandbox Code Playgroud)

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

在iOS上,这些方法实际上仅在iOS 8.0之后可用.

  • 您还可以使用以下Objective-C类别(BSD许可)在较旧的操作系统版本上使用这些新方法:https://github.com/petroules/CocoaBackports/blob/master/CocoaBackports/NSProcessInfo%2BPECocoaBackports.m它应该使用MRC,ARC或GC以及较旧的SDK回溯到OS X 10.5和iOS 2.0. (2认同)
  • 如果我已经知道我在10.10,我不需要检查!------在OS X 10.10上 (2认同)

Var*_*der 27

在命令行上:

$ sysctl kern.osrelease
kern.osrelease: 12.0.0
$ sysctl kern.osversion
kern.osversion: 12A269
Run Code Online (Sandbox Code Playgroud)

编程方式:

#include <errno.h>
#include <sys/sysctl.h>

char str[256];
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
Run Code Online (Sandbox Code Playgroud)

Darwin版本到OS X版本:

17.x.x. macOS 10.13.x High Sierra
16.x.x  macOS 10.12.x Sierra
15.x.x  OS X  10.11.x El Capitan
14.x.x  OS X  10.10.x Yosemite
13.x.x  OS X  10.9.x  Mavericks
12.x.x  OS X  10.8.x  Mountain Lion
11.x.x  OS X  10.7.x  Lion
10.x.x  OS X  10.6.x  Snow Leopard
 9.x.x  OS X  10.5.x  Leopard
 8.x.x  OS X  10.4.x  Tiger
 7.x.x  OS X  10.3.x  Panther
 6.x.x  OS X  10.2.x  Jaguar
 5.x    OS X  10.1.x  Puma
Run Code Online (Sandbox Code Playgroud)

获取和测试版本的示例:

#include <string.h>
#include <stdio.h>
#include <sys/sysctl.h>

/* kernel version as major minor component*/
struct kern {
    short int version[3];
};

/* return the kernel version */
void GetKernelVersion(struct kern *k) {
   static short int version_[3] = {0};
   if (!version_[0]) {
      // just in case it fails someday
      version_[0] = version_[1] = version_[2] = -1;
      char str[256] = {0};
      size_t size = sizeof(str);
      int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
      if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
    }
    memcpy(k->version, version_, sizeof(version_));
}

/* compare os version with a specific one
0 is equal
negative value if the installed version is less
positive value if the installed version is more
*/
int CompareKernelVersion(short int major, short int minor, short int component) {
    struct kern k;
    GetKernelVersion(&k);
    if ( k.version[0] !=  major) return major - k.version[0];
    if ( k.version[1] !=  minor) return minor - k.version[1];
    if ( k.version[2] !=  component) return component - k.version[2];
    return 0;
}

int main() {
   struct kern kern;
   GetKernelVersion(&kern);
   printf("%hd %hd %hd\n", kern.version[0], kern.version[1], kern.version[2]);

   printf("up: %d %d eq %d %d low %d %d\n",
        CompareKernelVersion(17, 0, 0), CompareKernelVersion(16, 3, 0),
        CompareKernelVersion(17, 3, 0), CompareKernelVersion(17,3,0),
        CompareKernelVersion(17,5,0), CompareKernelVersion(18,3,0));


}
Run Code Online (Sandbox Code Playgroud)

结果我的机器macOs High Sierra 10.13.2

17 3 0
up: -3 -1 eq 0 0 low 2 1
Run Code Online (Sandbox Code Playgroud)

  • 切勿使用它来确定OS X或iOS版本.虽然您现在可以通过算术确定OS X中的另一个版本,但这种比较并不能保证将来能够正常工作.作为一个具体的例子,一个iOS版本已经跳过了Darwin版本; OS X也是如此. (4认同)

iai*_*ain 24

有NSAppKitVersionNumber值可用于检查各种版本的AppKit,尽管它们与操作系统版本不完全对应

if (NSAppKitVersionNumber <= NSAppKitVersionNumber10_7_2) {
    NSLog (@"We are not running on Mountain Lion");
}
Run Code Online (Sandbox Code Playgroud)

  • 这也是官方文档所暗示的(在"运行时版本检查"下):https://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html (4认同)

小智 19

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

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

NSString * operatingSystemVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString];

NSLog(@"operatingSystemVersionString => %@" , operatingSystemVersionString);
Run Code Online (Sandbox Code Playgroud)

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

不会被弃用.


kai*_*jow 12

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

例如,要检查10.10或更高:

#include <CoreFoundation/CoreFoundation.h>
if (floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_9) {
    printf("On 10.10 or greater.");
}
Run Code Online (Sandbox Code Playgroud)

您需要链接CoreFoundation(或Foundation)框架.

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

import Foundation
if floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_8 {
    println("On 10.9 or greater.")
} else if floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_9 {
    println("On 10.10 or greater.")
}
Run Code Online (Sandbox Code Playgroud)


Vik*_*sal 11

您可以使用轻松获取操作系统的主要版本,次要版本 NSOperatingSystemVersion

NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];

NSString* major = [NSString stringWithFormat:@"%d", version.majorVersion];


NSString* minor = [NSString stringWithFormat:@"%d", version.minorVersion];


NSString* patch = [NSString stringWithFormat:@"%d", version.patchVersion];
Run Code Online (Sandbox Code Playgroud)

  • 仅限OS X 10.10和更高版本。 (2认同)

Win*_*ton 9

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

NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *productVersion = [version objectForKey:@"ProductVersion"];
NSLog (@"productVersion =========== %@", productVersion);
Run Code Online (Sandbox Code Playgroud)

我希望这可以帮助别人.

  • 但这不适用于沙盒应用程序. (4认同)

Mec*_*cki 9

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

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

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

static bool versionOK;
static unsigned versions[3];
static dispatch_once_t onceToken;

static
void initMacOSVersion ( void * unused ) {
    // `Gestalt()` actually gets the system version from this file.
    // Even `if (@available(macOS 10.x, *))` gets the version from there.
    CFURLRef url = CFURLCreateWithFileSystemPath(
        NULL, CFSTR("/System/Library/CoreServices/SystemVersion.plist"),
        kCFURLPOSIXPathStyle, false);
    if (!url) return;

    CFReadStreamRef readStr = CFReadStreamCreateWithFile(NULL, url);
    CFRelease(url);
    if (!readStr) return;

    if (!CFReadStreamOpen(readStr)) {
        CFRelease(readStr);
        return;
    }

    CFErrorRef outError = NULL;
    CFPropertyListRef propList = CFPropertyListCreateWithStream(
        NULL, readStr, 0, kCFPropertyListImmutable, NULL, &outError);
    CFRelease(readStr);
    if (!propList) {
        CFShow(outError);
        CFRelease(outError);
        return;
    }

    if (CFGetTypeID(propList) != CFDictionaryGetTypeID()) {
        CFRelease(propList);
        return;
    }

    CFDictionaryRef dict = propList;
    CFTypeRef ver = CFDictionaryGetValue(dict, CFSTR("ProductVersion"));
    if (ver) CFRetain(ver);
    CFRelease(dict);
    if (!ver) return;

    if (CFGetTypeID(ver) != CFStringGetTypeID()) {
        CFRelease(ver);
        return;
    }

    CFStringRef verStr = ver;
    // `1 +` for the terminating NUL (\0) character
    CFIndex size = 1 + CFStringGetMaximumSizeForEncoding(
        CFStringGetLength(verStr), kCFStringEncodingASCII);
    // `calloc` initializes the memory with all zero (all \0)
    char * cstr = calloc(1, size);
    if (!cstr) {
        CFRelease(verStr);
        return;
    }

    CFStringGetBytes(ver, CFRangeMake(0, CFStringGetLength(verStr)),
        kCFStringEncodingASCII, '?', false, (UInt8 *)cstr, size, NULL);
    CFRelease(verStr);

    printf("%s\n", cstr);

    int scans = sscanf(cstr, "%u.%u.%u",
        &versions[0], &versions[1], &versions[2]);
    free(cstr);
    // There may only be two values, but only one is definitely wrong.
    // As `version` is `static`, its zero initialized.
    versionOK = (scans >= 2);
}


static
bool macOSVersion (
    unsigned *_Nullable outMajor,
    unsigned *_Nullable outMinor,
    unsigned *_Nullable outBugfix
) {
    dispatch_once_f(&onceToken, NULL, &initMacOSVersion);
    if (versionOK) {
        if (outMajor) *outMajor = versions[0];
        if (outMinor) *outMinor = versions[1];
        if (outBugfix) *outBugfix = versions[2];
    }
    return versionOK;
}
Run Code Online (Sandbox Code Playgroud)

dispatch_once_f使用 a代替的原因dispatch_once是块也不是纯 C 语言。使用at的原因dispatch_once是版本号在系统运行时无法更改,因此没有理由在单个应用程序运行期间多次读取版本号。此外,读取它并不能保证万无一失,并且每次您的应用程序可能需要它时重新读取它都太昂贵。

谈论不安全,即使不太可能,读取它当然可能会失败,在这种情况下,函数返回false并且总是会返回false。我让您在应用程序代码中以有意义的方式处理这种情况,因为我不知道了解版本号对您的应用程序有多重要。如果版本号并不重要,也许您可​​以解决它,否则您应该以用户友好的方式让您的应用程序失败。

注意
如果您只需要版本号来根据系统版本执行替代代码,那么可能有更好的替代方法来自行查询版本号。有关详细信息,请参阅此问题。


Sen*_*tAI 7

如果您的应用程序需要在10.10以及之前的版本上运行,那么这是一个解决方案:

typedef struct {
        NSInteger majorVersion;
        NSInteger minorVersion;
        NSInteger patchVersion;
} MyOperatingSystemVersion;

if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)]) {
    MyOperatingSystemVersion version = ((MyOperatingSystemVersion(*)(id, SEL))objc_msgSend_stret)([NSProcessInfo processInfo], @selector(operatingSystemVersion));
    // do whatever you want with the version struct here
}
else {
    UInt32 systemVersion = 0;
    OSStatus err = Gestalt(gestaltSystemVersion, (SInt32 *) &systemVersion);
    // do whatever you want with the systemVersion as before
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 不要使用gestaltSystemVersion!如果任何版本组件大于9,则会失败.例如,OS X 10.4.11将返回为10.4.9.而是使用gestaltSystemVersionMajor,gestaltSystemVersionMinor和gestaltSystemVersionBugFix分别检索每个版本组件. (5认同)
  • 仅供参考,这仍然*在内部依赖于文件I/O ......每次调用它们时,两种方法都会读取SystemVersion.plist(无缓存). (2认同)

Tib*_*abo 6

这是我使用的:

NSInteger osxVersion;
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_6) {
    //10.6.x or earlier systems
    osxVersion = 106;
    NSLog(@"Mac OSX Snow Leopard");
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_7) {
    /* On a 10.7 - 10.7.x system */
    osxVersion = 107;
    NSLog(@"Mac OSX Lion");
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_8) {
    /* On a 10.8 - 10.8.x system */
    osxVersion = 108;
    NSLog(@"Mac OSX Moutain Lion");
} else {
    /* 10.9 or later system */
    osxVersion = 109;
    NSLog(@"Mac OSX: Mavericks or Later");
}
Run Code Online (Sandbox Code Playgroud)

建议在AppKit发行说明中使用

如果应用程序是沙盒,则无法读取/System/Library/CoreServices/SystemVersion.plist

  • "如果应用程序是沙盒的,则无法读取/System/Library/CoreServices/SystemVersion.plist" - 这是不正确的.根据Apple文档和我自己的经验,沙盒应用程序可以访问/ System中的世界可读项目.请参阅https://developer.apple.com/library/mac/documentation/security/conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW17 (2认同)

Mot*_*eor 6

这实际上是上面答案的汇编,有一些进一步指导有需要的开发人员.

OS-X以多种方式在运行时提供其版本.每种方式都更适合特定的开发方案.我会试着总结一下,如果我忘记了什么,希望别人能完成我的答案.

首先,获取操作系统版本的综合方法列表.

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

有关每个选项的更多详细信息 - 请参阅上面的答案.那里有很多信息!


Car*_*rum 5

uname(3):

uname()函数存储以nul结尾的信息字符串,用于将当前系统标识为引用的结构name.

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

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

  • 这实际上不起作用,因为`uname()`返回内核的版本,而不是营销版本(例如"10.8"). (2认同)
  • `uname()`对于获取移动设备模型(例如"iPad2,1")非常有用,所以......它并非完全没用.:) (2认同)

bfx*_*bfx 5

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

$ sysctl kern.osproductversion
kern.osproductversion: 10.14.3
Run Code Online (Sandbox Code Playgroud)

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

char str[256];
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osproductversion", str, &size, NULL, 0);
Run Code Online (Sandbox Code Playgroud)

对 xnu 内核代码库的提交表明这osproductversion是 2018 年中期的最新添加。我在 10.14.3 和 10.13.6 上成功测试了它。

总而言之,在我看来,最好的程序化、非 Cocoa 方法是检查是否kern.osproductversion产生有效的结果,否则返回到本答案中kern.osrelease所述的手动映射。