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

时间:2012-06-17 16:09:16

标签: ios macos cocoa macos-carbon

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

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

可以使用哪些其他API在运行时在Cocoa应用程序中检查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);
}

16 个答案:

答案 0 :(得分:62)

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

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

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

- (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version

小心,虽然记录在OS X 10.10及更高版本中可用,但在OS X 10.9(probably 10.9.2)上存在operatingSystemVersionisOperatingSystemAtLeastVersion:并且工作正常正如所料。这意味着您不能测试NSProcessInfo是否响应这些选择器以检查您是否在OS X 10.9或10.10上运行。

在iOS上,这些方法实际上仅在iOS 8.0以后才可用。

答案 1 :(得分:27)

在命令行上:

$ sysctl kern.osrelease
kern.osrelease: 12.0.0
$ sysctl kern.osversion
kern.osversion: 12A269

编程:

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

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

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

获取和测试版本的示例:

#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));


}

结果我的机器macOs High Sierra 10.13.2

17 3 0
up: -3 -1 eq 0 0 low 2 1

答案 2 :(得分:24)

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

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

答案 3 :(得分:19)

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

获取操作系统版本字符串的代码在下面..

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

NSLog(@"operatingSystemVersionString => %@" , operatingSystemVersionString);

// ===&gt;&gt;版本10.8.2(Build 12C2034)结果值

不推荐

答案 4 :(得分: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.");
}

您需要链接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.")
}

答案 5 :(得分: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];

答案 6 :(得分:9)

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

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

我希望这有助于某人。

答案 7 :(得分:8)

如果您的应用需要在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
}

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

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

答案 8 :(得分: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");
}

建议AppKit Release Notes

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

答案 9 :(得分:6)

这实际上是对上述答案的汇编,并向有需要的开发人员提供了一些进一步的指导。

OS-X以多种方式在运行时提供其版本。每种方式都更适合特定的开发方案。我会尝试总结所有这些,并希望如果我忘记某些事情,别人会完成我的答案。

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

  1. uname命令行工具和函数提供操作系统的unix(darwin)版本。虽然这不是操作系统的营销版本,但它与其唯一对齐,因此您可以从中推断出OS-X营销版本。
  2. sysctl kern.osrelease命令行(或sysctlbyname("kern.osrelease", str, &size, NULL, 0)函数)将提供与uname相同的信息,稍微容易解析。
  3. Gestalt(gestaltSystemVersionMajor)(其&#34; Minor&#34;和BugFix&#34;变种是获取营销操作系统的最早(前碳!)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. 的全面枚举
  7. [[NSProcessInfo processInfo] operatingSystemVersionString];是Obj-C中可用于OS-X和iOS应用程序的Cocoa API。
  8. /System/Library/CoreServices/SystemVersion.plist中有一个资源.plist,其中包含&#34; ProductVersion&#34;中的操作系统版本。键。 NSProcessInfo从此文件中读取其信息,但您可以使用所选的PList-reading API直接执行此操作。
  9. 有关每个选项的更多详细信息,请参阅上面的答案。那里有很多信息!

答案 10 :(得分:5)

uname(3)

  

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

     

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

     
      
  • sysname - 操作系统实施的名称。
  •   
  • nodename - 此计算机的网络名称。
  •   
  • release - 操作系统的发布级别。
  •   
  • version - 操作系统的版本级别。
  •   
  • machine - 计算机硬件平台。
  •   

答案 11 :(得分:3)

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

$ sysctl kern.osproductversion
kern.osproductversion: 10.14.3

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

char str[256];
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osproductversion", str, &size, NULL, 0);
xnu内核代码库中的

This commit表示osproductversion是2018年中期的最新添加。我在10.14.3和10.13.6上成功进行了测试。

总而言之,我认为最好的程序化非可可方法是检查kern.osproductversion是否产生有效结果并退回到kern.osrelease并按照{{3 }}。

答案 12 :(得分:0)

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

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

def __dictionary__(self):
    return {name: getattr(self, name, None)
            for name in self.__slots__}

这将导致:

sw_vers

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

ProductName:    Mac OS X
ProductVersion: 10.13.6
BuildVersion:   17G65

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

答案 13 :(得分:0)

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, ...)编译器功能也从那里获取版本号的地方(我深入研究系统来了解这一点)。即使Swift的#available(macOS 10.x, *)也会从那里获得版本号,我也不会感到惊讶。

没有简单的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;
}

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

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

答案 14 :(得分:0)

///我为Swift(Multiplatform)支付了2美分

#if os(iOS)
import UIKit
#elseif os(OSX)
import Cocoa
#endif



func platform() -> String {
    var systemInfo = utsname()
    uname(&systemInfo)
    let size = Int(_SYS_NAMELEN) // is 32, but posix AND its init is 256....
    
    let s = withUnsafeMutablePointer(to: &systemInfo.machine) {p in
        //    let s = withUnsafeMutablePointer(to: &systemInfo.nodename) {p in
        
        p.withMemoryRebound(to: CChar.self, capacity: size, {p2 in
            return String(cString: p2)
        })
        
    }
    return s
}



func AppName()->String{
    let bund = Bundle.main
    if let displayName = bund.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String {
        if displayName.count>0{
            return displayName
        }
    }
    
    if let name = bund.object(forInfoDictionaryKey: "CFBundleName") as? String {
        return name
    }
    return "no AppName"
}


func buildVers()->String{
    
    let bund = Bundle.main
    let vers = bund.object(forInfoDictionaryKey: "CFBundleVersion") as! String
    return  vers
}


func AppleStoreVers()->String{
    
    if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        return version
    }
    return "no.vers."
}



#if os(iOS)
func systemVersion()->String{
    let  v = UIDevice.current.systemVersion
    return v
}
#elseif os(OSX)
#endif

答案 15 :(得分:0)

针对macOS Catalina和更高版本的更新

20.x.x  macOS 11.X.X  BigSur
19.x.x  macOS 10.15.x Catalina
18.x.x  macOS 10.14.x Mojave
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