OS X 또는 iOS (Gestalt를 사용하지 않고)에서 런타임시 OS 버전을 어떻게 확인합니까?
에있는 Gestalt () 함수 CarbonCore/OSUtils.h
는 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) 에서는 다음과 같이 정의 된 구조체 [[NSProcessInfo processInfo] operatingSystemVersion]
를 반환하는 것을 사용할 수 있습니다.NSOperatingSystemVersion
typedef struct {
NSInteger majorVersion;
NSInteger minorVersion;
NSInteger patchVersion;
} NSOperatingSystemVersion;
NSProcessInfo에는 비교를 수행하는 메서드도 있습니다.
- (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version
주의 OS X 10.10 이후에 출시 될 문서화 있지만, 모두 operatingSystemVersion
와 isOperatingSystemAtLeastVersion:
OS X 10.9 (에 존재 아마 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);
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
OS 버전과 정확히 일치하지는 않지만 다양한 버전의 AppKit을 확인하는 데 사용할 수있는 NSAppKitVersionNumber 값이 있습니다.
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에 의존하지 않습니다.
이것이 내가 사용하는 것입니다.
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를 읽을 수 없습니다.
이것은 실제로 위의 답변 모음이며 필요한 개발자에게 추가 지침이 있습니다.
OS-X는 여러 가지 방법으로 런타임에 버전을 제공합니다. 각 방식은 특정 개발 시나리오에 더 적합합니다. 나는 그것들을 모두 요약하려고 노력할 것이며 내가 무언가를 잊었을 때 다른 사람들이 내 대답을 완성하기를 바랍니다.
첫째, OS 버전을 얻는 방법의 포괄적 인 목록입니다.
uname
명령 줄 도구 및 기능은 OS의 UNIX (다윈) 버전을 제공한다. 이것은 OS의 마케팅 버전은 아니지만 고유하게 정렬되어 있으므로 OS-X 마케팅 버전을 추론 할 수 있습니다.sysctl kern.osrelease
명령 줄 (또는sysctlbyname("kern.osrelease", str, &size, NULL, 0)
함수)은 uname과 동일한 정보를 제공하며 구문 분석이 약간 더 쉽습니다.Gestalt(gestaltSystemVersionMajor)
( "Minor
"및BugFix
"변형은 마케팅 OS 버전을 얻기위한 가장 오래된 (카본 이전!) API이며, 오랫동안 사용되지 않는 상태에서 계속 지원됩니다. CoreServices 프레임 워크에서 C로 제공되지만 권장되지는 않습니다.NSAppKitVersionNumber
AppKit 프레임 워크의 부동 상수로, OS-X Appkit 버전 (OS 버전에 맞춰 조정 됨)을 제공하며 AppKit에 대해 링크되는 모든 애플리케이션에서 사용할 수 있습니다. 또한 가능한 모든 버전의 포괄적 인 목록을 제공합니다 (예를 들어NSAppKitVersionNumber10_7_2
)kCFCoreFoundationVersionNumber
C, Obj-C 및 Swift 모두에서 CoreFoundation에 연결된 모든 앱에 사용할 수있는 Appkit 대응 요소와 동일한 CoreFoundation 프레임 워크 부동 상수입니다. 그것은 너무 모든 OS의 X 출시 버전에 비해 포괄적 열거 (예를 제공kCFCoreFoundationVersionNumber10_9
)[[NSProcessInfo processInfo] operatingSystemVersionString];
Obj-C에서 OS-X 및 iOS 애플리케이션 모두에 사용할 수있는 Cocoa API입니다./System/Library/CoreServices/SystemVersion.plist
무엇보다도 "ProductVersion"키에 OS 버전이 포함 된 리소스 .plist가 있습니다. NSProcessInfo는이 파일에서 정보를 읽지 만 선택한 PList 읽기 API를 사용하여 직접이 작업을 수행 할 수 있습니다.
각 옵션에 대한 자세한 내용은 위의 답변을 참조하십시오. 거기에 많은 정보가 있습니다!
있습니다 uname(3)
:
이
uname()
함수는 현재 시스템을 식별하는 정보의 null로 끝나는 문자열을에서 참조하는 구조에 저장name
합니다.
utsname
구조는에 정의 된<sys/utsname.h>
헤더 파일, 그리고 다음과 같은 멤버들을 포함하고있다 :
sysname
-운영 체제 구현의 이름입니다.nodename
-이 컴퓨터의 네트워크 이름.release
-운영 체제의 릴리스 수준.version
-운영 체제의 버전 수준.machine
-기계 하드웨어 플랫폼.
sysctl
오늘 macOS 에서 놀면서 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에서 성공적으로 테스트했습니다.
대체로 내 생각에 가장 좋은 프로그래밍 방식의 비 Cocoa 접근 방식은 kern.osproductversion
유효한 결과를 생성 하는지 확인 하고이 답변kern.osrelease
에서 설명한대로 수동 매핑으로 대체하는 것 입니다.
게임이 늦었지만 여기에서 답을 찾기도했습니다. 가치가있는 것이라면 다른 사람에게 유용 할 수도 있습니다.
과거에는 명령 줄 방식을 사용했습니다.
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에서 더 이상 업데이트하지 않습니다. 상수 사용은 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 호출이 없기 때문에 CoreFoundation
자체적으로 순수한 C 프레임 워크 인 프레임 워크 만 필요한 다음과 같은 순수한 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;
}
The reason for using a dispatch_once_f
instead of dispatch_once
is that blocks aren't pure C either. The reason for using dispatch once at all is that the version number cannot change while the system is running, so there is no reason for reading the version number more than once during a single app run. Also reading it is not guaranteed to be fail-safe and too expensive to re-read it every time your app may require it.
Talking about not being fail-safe, even though it is unlikely, reading it may, of course, fail, in which case the function returns false
and always will return false
. I leave it up to you to handle that case in a meaningful way in your app code because I cannot know how critical knowing the version number is to your app. If the version number is not critical, maybe you can work around it, otherwise you should make your app fail in a user friendly way.
'Development Tip' 카테고리의 다른 글
32/64 비트 또는 CPU 용 C # 컴파일? (0) | 2020.11.09 |
---|---|
Scala에서 한 번에 여러 예외 잡기 (0) | 2020.11.09 |
콘솔 애플리케이션의 진행률 표시 줄 (0) | 2020.11.09 |
Laravel Advanced 함수에 변수를 전달하는 방법은 어디입니까? (0) | 2020.11.09 |
HTML에서 공백없이 긴 줄을 감싸는 방법은 무엇입니까? (0) | 2020.11.09 |