web-dev-qa-db-ja.com

OS XまたはiOS(Gestaltを使用しない)で実行時にOSバージョンを確認するにはどうすればよいですか?

CarbonCore/OSUtils.hにあるGestalt()関数は、OS X 10.8 Mountain Lionで非推奨になりました。

私はよくこの関数を使用して、実行時にOS Xオペレーティングシステムのバージョンをテストします(以下のおもちゃの例を参照)。

Cocoaアプリケーションで実行時にOS Xオペレーティングシステムのバージョンを確認するために使用できる他のAPIは何ですか?

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);
}
66

OS X 10.10(およびiOS 8.0)では、次のように定義されるNSOperatingSystemVersion構造体を返す[[NSProcessInfo processInfo] operatingSystemVersion]を使用できます。

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

NSProcessInfoには、比較を行うメソッドもあります。

- (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version

注意してください、OS X 10.10以降で利用可能と記載されていますが、OS X 10.9にはoperatingSystemVersionisOperatingSystemAtLeastVersion:の両方が存在します( おそらく10.9.2 )そして期待どおりに動作します。つまり、NSProcessInfoがこれらのセレクターに応答するかどうかをテストして、OS X 10.9または10.10で実行しているかどうかを確認してはいけません。

IOSでは、これらのメソッドはiOS 8.0以降でのみ有効です。

63
0xced

コマンドラインで:

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

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
27

NSAppKitVersionNumber値があり、これを使用してAppKitのさまざまなバージョンを確認できますが、OSバージョンに正確に対応しているわけではありません

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

ココアAPIがあります。 NSProcessInfoクラスからos Xバージョン文字列を取得できます。

オペレーティングシステムのバージョン文字列を取得するコードは以下のとおりです。

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

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

// === >>バージョン10.8.2(ビルド12C2034)の結果値

それではないは非推奨です。

19
DHL

サポートする最小バージョンのみを確認する必要がある場合に使用できる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.")
}
12
kainjow

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];
11
Vikas Bansal

または、もっと簡単に言うと、コードは次のとおりです。

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

これが誰かの助けになることを願っています。

9
Winston

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
SentientAI

これは、実際には上記の回答をまとめたもので、必要な開発者をさらにガイドしています。

OS-Xは、いくつかの方法でランタイムにバージョンを提供します。それぞれの方法は、特定の開発シナリオにより適しています。私はそれらすべてを要約しようとします、そしてもし何かを忘れたら他の人が私の答えを完成することを望みます。

まず、OSバージョンを取得する方法の包括的なリスト。

  1. unameコマンドラインツールおよび関数は、OSのUNIX(darwin)バージョンを提供します。これはOSのマーケティングバージョンではありませんが、一意に調整されているため、OS-Xマーケティングバージョンを推測できます。
  2. _sysctl kern.osrelease_コマンドライン(またはsysctlbyname("kern.osrelease", str, &size, NULL, 0)関数)はunameと同じ情報を提供し、解析がやや簡単になります。
  3. Gestalt(gestaltSystemVersionMajor)( "Minor"およびBugFix "バリアントを含む)は、マーケティングOSバージョンを取得するための最も古い(pre-Carbon!)APIであり、長い間非推奨となっています。 CoreServicesフレームワークからCで入手できますが、推奨されません。
  4. NSAppKitVersionNumberはAppKitフレームワークの浮動小数点定数で、OS-X Appkitバージョン(OSバージョンに合わせて)を提供し、AppKitにリンクするすべてのアプリケーションで使用できます。また、可能なすべてのバージョンの包括的な列挙も提供します(例:_NSAppKitVersionNumber10_7_2_)
  5. kCFCoreFoundationVersionNumberは、CoreFoundationフレームワークのfloat定数で、Appkitの同等物と同じで、C、Obj-C、Swiftの両方でCoreFoundationにリンクされたすべてのアプリで使用できます。また、すべてのOS Xリリースバージョンの包括的な列挙を提供します(例:_kCFCoreFoundationVersionNumber10_9_)
  6. _[[NSProcessInfo processInfo] operatingSystemVersionString];_は、Obj-CでOS-XアプリケーションとiOSアプリケーションの両方で使用できるCocoa APIです。
  7. _/System/Library/CoreServices/SystemVersion.plist_にはリソース.plistがありますが、これには「ProductVersion」キーにOSバージョンが含まれています。 NSProcessInfoはこのファイルから情報を読み取りますが、選択したPList読み取りAPIを使用して直接これを行うことができます。

各オプションの詳細については、上記の回答をご覧ください。そこにはたくさんの情報があります!

6
Motti Shneor

これは私が使用するものです:

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リリースノート で推奨されています

アプリがサンドボックス化されている場合、/ System/Library/CoreServices/SystemVersion.plistを読み取ることはできません。

6
Tibidabo

uname(3) があります:

uname()関数は、nameによって参照される構造体に現在のシステムを識別する情報のヌル終了文字列を格納します

utsname構造は<sys/utsname.h>ヘッダーファイル。次のメンバーが含まれます。

  • sysname-オペレーティングシステム実装の名前。
  • nodename-このマシンのネットワーク名。
  • release-オペレーティングシステムのリリースレベル。
  • version-オペレーティングシステムのバージョンレベル。
  • machine-マシンハードウェアプラットフォーム。
5
Carl Norum

今日、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カーネルコードベースは、osproductversionが2018年半ばに追加された最近の追加であることを示しています。10.14.3および10.13.6で正常にテストしました。

全体として、私の意見では、最高のプログラマチックな非ココアアプローチは、kern.osproductversionは有効な結果を生成し、kern.osreleaseおよび this answer で説明されている手動マッピング。

3
bfx

ゲームに遅刻したが、私もここで答えを探していた。それが価値があるものについては、多分それは他の誰かのために役立つでしょう。

以前は、コマンドラインアプローチを使用していました。

sw_vers

結果:

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

各行は個別に要求できます(キャメルバック表記に注意してください)。

sw_vers -productVersion
10.13.6

sw_vers -productName
Mac OS X

sw_vers -buildVersion
17G65

そうは言っても、ここにリストされている他のすべてのソリューションに感謝します...

0
Hanzaplastique

Gestalt()は純粋なC APIです。受け入れられた答えは、NSProcessInfoの使用を示唆していますが、Objective-Cコードが必要であり、何らかの理由で純粋なCが必要な場合は使用できません。同じことがNSAppKitVersionNumberにも当てはまります。これは、10.13.4。以降、Appleによって更新されません。Cコードでは定数kCFCoreFoundationVersionNumberを使用できましたが、この定数は10.11.4以降は更新されていません。Cでsysctlを使用して_kern.osrelease_を読み取ることは可能ですが、マッピングテーブルまたは現在のカーネルバージョン管理スキームに依存するため、カーネルとしての将来のバージョンでは信頼できませんバージョンスキームが変更される可能性があり、マッピングテーブルは将来のバージョンを知ることができません。

Apple=)からの公式で推奨される方法は_/System/Library/CoreServices/SystemVersion.plist_を読むことです。これはGestalt()がバージョン番号を取得する場所でもあり、NSProcessInfo()はバージョン番号を取得します。これはコマンドラインツール_sw_vers_がバージョン番号を取得する場所でもあり、新しい@available(macOS 10.x, ...)コンパイラー機能がバージョン番号を取得する場所でもあります(I 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を返します。バージョン番号がアプリにとってどれほど重要であるかを知ることができないため、アプリコードでそのケースを意味のある方法で処理するのはあなた次第です。バージョン番号が重要でない場合は、回避することができます。そうでない場合は、ユーザーフレンドリーな方法でアプリを失敗させる必要があります。

0
Mecki