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);
}
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にはoperatingSystemVersion
とisOperatingSystemAtLeastVersion:
の両方が存在します( おそらく10.9.2 )そして期待どおりに動作します。つまり、NSProcessInfo
がこれらのセレクターに応答するかどうかをテストして、OS X 10.9または10.10で実行しているかどうかを確認してはいけません。
IOSでは、これらのメソッドはiOS 8.0以降でのみ有効です。
コマンドラインで:
$ 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
NSAppKitVersionNumber値があり、これを使用してAppKitのさまざまなバージョンを確認できますが、OSバージョンに正確に対応しているわけではありません
if (NSAppKitVersionNumber <= NSAppKitVersionNumber10_7_2) {
NSLog (@"We are not running on Mountain Lion");
}
ココアAPIがあります。 NSProcessInfoクラスからos Xバージョン文字列を取得できます。
オペレーティングシステムのバージョン文字列を取得するコードは以下のとおりです。
NSString * operatingSystemVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString];
NSLog(@"operatingSystemVersionString => %@" , operatingSystemVersionString);
// === >>バージョン10.8.2(ビルド12C2034)の結果値
それではないは非推奨です。
サポートする最小バージョンのみを確認する必要がある場合に使用できる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.")
}
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];
または、もっと簡単に言うと、コードは次のとおりです。
NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *productVersion = [version objectForKey:@"ProductVersion"];
NSLog (@"productVersion =========== %@", productVersion);
これが誰かの助けになることを願っています。
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に依存しません。
これは、実際には上記の回答をまとめたもので、必要な開発者をさらにガイドしています。
OS-Xは、いくつかの方法でランタイムにバージョンを提供します。それぞれの方法は、特定の開発シナリオにより適しています。私はそれらすべてを要約しようとします、そしてもし何かを忘れたら他の人が私の答えを完成することを望みます。
まず、OSバージョンを取得する方法の包括的なリスト。
uname
コマンドラインツールおよび関数は、OSのUNIX(darwin)バージョンを提供します。これはOSのマーケティングバージョンではありませんが、一意に調整されているため、OS-Xマーケティングバージョンを推測できます。sysctl kern.osrelease
_コマンドライン(またはsysctlbyname("kern.osrelease", str, &size, NULL, 0)
関数)はunameと同じ情報を提供し、解析がやや簡単になります。Gestalt(gestaltSystemVersionMajor)
( "Minor
"およびBugFix
"バリアントを含む)は、マーケティングOSバージョンを取得するための最も古い(pre-Carbon!)APIであり、長い間非推奨となっています。 CoreServicesフレームワークからCで入手できますが、推奨されません。NSAppKitVersionNumber
はAppKitフレームワークの浮動小数点定数で、OS-X Appkitバージョン(OSバージョンに合わせて)を提供し、AppKitにリンクするすべてのアプリケーションで使用できます。また、可能なすべてのバージョンの包括的な列挙も提供します(例:_NSAppKitVersionNumber10_7_2
_)kCFCoreFoundationVersionNumber
は、CoreFoundationフレームワークのfloat定数で、Appkitの同等物と同じで、C、Obj-C、Swiftの両方でCoreFoundationにリンクされたすべてのアプリで使用できます。また、すべてのOS Xリリースバージョンの包括的な列挙を提供します(例:_kCFCoreFoundationVersionNumber10_9
_)[[NSProcessInfo processInfo] operatingSystemVersionString];
_は、Obj-CでOS-XアプリケーションとiOSアプリケーションの両方で使用できるCocoa APIです。/System/Library/CoreServices/SystemVersion.plist
_にはリソース.plistがありますが、これには「ProductVersion」キーにOSバージョンが含まれています。 NSProcessInfoはこのファイルから情報を読み取りますが、選択したPList読み取りAPIを使用して直接これを行うことができます。各オプションの詳細については、上記の回答をご覧ください。そこにはたくさんの情報があります!
これは私が使用するものです:
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を読み取ることはできません。
uname(3)
があります:
uname()
関数は、name
によって参照される構造体に現在のシステムを識別する情報のヌル終了文字列を格納します。
utsname
構造は<sys/utsname.h>
ヘッダーファイル。次のメンバーが含まれます。
sysname
-オペレーティングシステム実装の名前。nodename
-このマシンのネットワーク名。release
-オペレーティングシステムのリリースレベル。version
-オペレーティングシステムのバージョンレベル。machine
-マシンハードウェアプラットフォーム。
今日、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 で説明されている手動マッピング。
ゲームに遅刻したが、私もここで答えを探していた。それが価値があるものについては、多分それは他の誰かのために役立つでしょう。
以前は、コマンドラインアプローチを使用していました。
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
そうは言っても、ここにリストされている他のすべてのソリューションに感謝します...
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
を返します。バージョン番号がアプリにとってどれほど重要であるかを知ることができないため、アプリコードでそのケースを意味のある方法で処理するのはあなた次第です。バージョン番号が重要でない場合は、回避することができます。そうでない場合は、ユーザーフレンドリーな方法でアプリを失敗させる必要があります。