Как определить версию ОС во время выполнения в OS X или iOS (без использования Gestalt)?
Функция Gestalt(), расположенная в CarbonCore/OSUtils.h
было объявлено устаревшим с OS X 10.8 Mountain Lion.
Я часто использую эту функцию для тестирования версии операционной системы OS X во время выполнения (см. Игрушечный пример ниже).
Какой еще 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);
}
17 ответов
На 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);
Дарвин версия до выпуска 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, хотя они не точно соответствуют версиям ОС.
if (NSAppKitVersionNumber <= NSAppKitVersionNumber10_7_2) {
NSLog (@"We are not running on Mountain Lion");
}
Есть какао API. Вы можете получить строку версии OS X из класса NSProcessInfo.
Код для получения строки версии операционной системы приведен ниже.
NSString * operatingSystemVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString];
NSLog(@"operatingSystemVersionString => %@" , operatingSystemVersionString);
// === >> Версия 10.8.2 (сборка 12C2034) результирующее значение
Это не считается устаревшим.
Вы можете легко получить мажорную, минорную и исправленную версии операционной системы, используя 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];
Существует также 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.")
}
Или, проще говоря, вот код:
NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
NSString *productVersion = [version objectForKey:@"ProductVersion"];
NSLog (@"productVersion =========== %@", productVersion);
Я надеюсь, что это помогает кому-то.
Gestalt()
это чистый C API. Принятый ответ предполагает использование NSProcessInfo
но для этого требуется код Objective-C, и его нельзя использовать, если по какой-то причине требуется чистый C. То же самое относится и к NSAppKitVersionNumber
, который также больше не обновляется Apple с 10.13.4. Используя константу kCFCoreFoundationVersionNumber
было возможно в коде C, но эта константа больше не обновлялась с 10.11.4. чтение kern.osrelease
с помощью sysctl
возможно в C, но требует таблицы сопоставления или полагается на текущую схему управления версиями ядра и, таким образом, не является надежной для будущей версии, поскольку схема версии ядра может измениться, и таблица сопоставления не может знать будущие версии.
Официальный и рекомендуемый способ от Apple, чтобы прочитать /System/Library/CoreServices/SystemVersion.plist
поскольку это также где Gestalt()
получает номер версии, это также где NSProcessInfo()
получает номер версии, это также где инструмент командной строки sw_vers
получает номер версии, и это даже там, где новый @available(macOS 10.x, ...)
Функция компилятора получает номер версии (я углубился в систему, чтобы узнать об этом). Я не удивлюсь, если даже Свифт #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
в том, что блоки тоже не чистые. Причина, по которой диспетчер используется один раз, заключается в том, что номер версии не может измениться во время работы системы, поэтому нет необходимости читать номер версии более одного раза за один запуск приложения. Кроме того, его чтение не гарантирует отказоустойчивость и слишком дорого перечитывает его каждый раз, когда ваше приложение может потребовать этого.
Говоря о том, что он не является отказоустойчивым, даже если это маловероятно, его чтение может, конечно, потерпеть неудачу, и в этом случае функция возвращает false
и всегда вернусь false
, Я оставляю на ваше усмотрение осмысленное рассмотрение этого случая в коде вашего приложения, потому что я не могу понять, насколько важно знать номер версии для вашего приложения. Если номер версии не является критичным, возможно, вы можете обойти его, в противном случае вы должны заставить свое приложение отказывать в удобной для пользователя форме.
Это на самом деле компиляция ответов, приведенных выше, с некоторыми дальнейшими рекомендациями для нуждающегося разработчика.
OS-X предоставляет свою версию во время выполнения несколькими способами. Каждый способ лучше подходит для конкретного сценария развития. Я постараюсь обобщить их все и надеюсь, что другие ответят на мой вопрос, если я что-то забуду.
Во-первых, исчерпывающий список способов получения версии ОС.
uname
инструмент командной строки и функция обеспечивает Unix (Дарвин) версию ОС. Хотя это не маркетинговая версия ОС, она однозначно соответствует ей, поэтому из нее можно вывести маркетинговую версию OS-X.sysctl kern.osrelease
командная строка (илиsysctlbyname("kern.osrelease", str, &size, NULL, 0)
function) будет предоставлять ту же информацию, что и uname, немного легче разобрать.Gestalt(gestaltSystemVersionMajor)
(с этими "Minor
" а такжеBugFix
"Варианты - это самый старый (до-Carbon!) API для получения маркетинговой версии ОС, все еще поддерживаемый давно устаревшим. Доступен в C из инфраструктуры CoreServices, но не рекомендуется.NSAppKitVersionNumber
является константой с плавающей запятой платформы AppKit, которая будет предоставлять версию OS-X Appkit (в соответствии с версией ОС), доступную для всех приложений, которые ссылаются на AppKit. Он также предоставляет полный перечень всех возможных версий (например,NSAppKitVersionNumber10_7_2
)kCFCoreFoundationVersionNumber
является плавающей константой платформы CoreFoundation, идентичной аналогу Appkit, доступной для всех приложений, связанных с CoreFoundation, как в C, Obj-C, так и в Swift. Он также предоставляет исчерпывающее перечисление для всех выпущенных версий OS X (например,kCFCoreFoundationVersionNumber10_9
)[[NSProcessInfo processInfo] operatingSystemVersionString];
API-интерфейс Cocoa, доступный в Obj-C для приложений OS-X и iOS.- Существует ресурс.plist в
/System/Library/CoreServices/SystemVersion.plist
который, помимо прочего, содержит версию ОС в ключе "ProductVersion". NSProcessInfo считывает информацию из этого файла, но вы можете сделать это напрямую, используя выбранный вами API чтения PList.
Для более подробной информации о каждом варианте - пожалуйста, обратитесь к ответам выше. Там много информации!
Если у вас есть приложение, которое должно работать на 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, поэтому я думаю, что это был только частный API в 10.9 (но все еще работает).
Это работает во всех версиях OS X и не зависит от анализа строк или файлового ввода-вывода.
Это то, что я использую:
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 невозможно, если приложение находится в "песочнице"
Играя с sysctl
на macOS сегодня наткнулся kern.osproductversion
которая действительно содержит текущую версию ОС. На моем Mac под управлением Mojave я получаю:
$ 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
и ручное отображение, как описано в этом ответе в противном случае.
Там в uname(3)
:
uname()
функция хранит нуль-терминированные строки информации, идентифицирующие текущую систему, в структуре, на которую ссылаетсяname
,
utsname
структура определяется в<sys/utsname.h>
заголовочный файл, и содержит следующие члены:
sysname
- Имя операционной системы реализации.nodename
- Сетевое имя этой машины.release
- Уровень выпуска операционной системы.version
- Уровень версии операционной системы.machine
- аппаратная платформа оборудования.
Поздно к игре, но я в конечном итоге здесь в поисках ответа. Для чего это стоит, может быть, это полезно для кого-то другого;
В прошлом я использовал подход командной строки:
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
Сказав это, спасибо за все другие решения, перечисленные здесь...
// мои два цента за Swift (мультиплатформенный)
#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
Обновление для 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
2022, МакОС
let a = ProcessInfo.processInfo.operatingSystemVersion
let b = ProcessInfo.processInfo.operatingSystemVersionString
iOS:
var a = UIDevice.current.systemVersion
let b = ProcessInfo.processInfo.operatingSystemVersion
let c = ProcessInfo.processInfo.operatingSystemVersionString