Hoe bepaal ik de OS-versie tijdens runtime in OS X of iOS (zonder Gestalt te gebruiken)?

De functie Gestalt() in CarbonCore/OSUtils.his verouderd vanaf OS X 10.8 Mountain Lion.

Ik gebruik deze functie vaak om de versie van het OS X-besturingssysteem tijdens runtime te testen (zie het speelgoedvoorbeeld hieronder).

Welke andere API kan worden gebruikt om de versie van het OS X-besturingssysteem tijdens runtime te controleren in een Cocoa-toepassing?

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

Antwoord 1, autoriteit 100%

Op OS X 10.10 (en iOS 8.0) kun je [[NSProcessInfo processInfo] operatingSystemVersion]die een NSOperatingSystemVersionstruct, gedefinieerd als

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

Er is ook een methode in NSProcessInfodie doe de vergelijking voor je:

- (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version

Pas op, hoewel gedocumenteerd is dat ze beschikbaar zijn in OS X 10.10 en hoger, bestaan ​​zowel operatingSystemVersionals isOperatingSystemAtLeastVersion:op OS X 10.9 (waarschijnlijk 10.9.2) en werken zoals verwacht. Het betekent dat je niet moet testen of NSProcessInforeageert op deze selectors om te controleren of je OS X 10.9 of 10.10 gebruikt.

Op iOS zijn deze methoden in feite pas beschikbaar sinds iOS 8.0.


Antwoord 2, autoriteit 37%

Op de opdrachtregel:

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

Programmatisch:

#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-versie naar OS X-release:

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

Een voorbeeld om versies te krijgen en te testen:

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

Resultaat op mijn machine macOs High Sierra 10.13.2

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

Antwoord 3, autoriteit 34%

Er is de NSAppKitVersionNumber-waarde die u kunt gebruiken om de verschillende versies van AppKit te controleren, hoewel deze niet exact overeenkomen met de OS-versies

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

Antwoord 4, autoriteit 27%

Er is een cacao-API. U kunt een os X-versiereeks ophalen uit de klasse NSProcessInfo.

De code voor het verkrijgen van de besturingssysteemversiereeks staat hieronder..

NSString * operatingSystemVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString];
NSLog(@"operatingSystemVersionString => %@" , operatingSystemVersionString);

// ===>> Versie 10.8.2 (Build 12C2034) resultaatwaarde

Het nietis verouderd.


Antwoord 5, autoriteit 16%

Er is ook kCFCoreFoundationVersionNumber dat kan worden gebruikt als u alleen hoeft te controleren of er een minimale versie is om te ondersteunen. Dit heeft als voordeel dat het werkt vanaf 10.1 en kan worden gedaan in C, C++ en Objective-C.

Bijvoorbeeld om te controleren op 10.10 of hoger:

#include <CoreFoundation/CoreFoundation.h>
if (floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_9) {
    printf("On 10.10 or greater.");
}

U moet een koppeling maken met het CoreFoundation (of Foundation) raamwerk.

Het werkt ook in Swift op exact dezelfde manier. Hier is nog een voorbeeld:

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.")
}

Antwoord 6, autoriteit 16%

U kunt eenvoudig de belangrijkste, kleine, patchversie van het besturingssysteem verkrijgen met behulp van 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];

Antwoord 7, autoriteit 12%

Of, om het simpeler te zeggen, hier is de code:

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

Ik hoop dat dit iemand helpt.


Antwoord 8, autoriteit 11%

Als je een app hebt die zowel op 10.10 als op eerdere versies moet draaien, is hier een oplossing:

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
}

Merk op dat zelfs 10.9 lijkt te reageren op de besturingssysteemversie-selector, dus ik denk dat het gewoon een privé-API was in 10.9 (maar werkt nog steeds).

Dit werkt op alle versies van OS X en is niet afhankelijk van string-parsing of bestands-I/O.


Antwoord 9, autoriteit 10%

Dit is eigenlijk een compilatie van bovenstaande antwoorden, met wat verdere richtlijnen voor de ontwikkelaar in nood.

OS-X biedt zijn versie op verschillende manieren in runtime. Elke manier past beter bij een specifiek ontwikkelingsscenario. Ik zal proberen ze allemaal samen te vatten, en ik hoop dat anderen mijn antwoord zullen aanvullen als ik iets vergeten ben.

Eerst de uitgebreide lijst met manieren om de OS-versie te verkrijgen.

  1. De unameopdrachtregeltool en functie biedt de unix (darwin) versie van het besturingssysteem. Hoewel dit niet de marketingversie van het besturingssysteem is, is het er uniek op afgestemd, zodat u er de OS-X-marketingversie uit kunt afleiden.
  2. De opdrachtregel

  3. sysctl kern.osrelease(of de functie sysctlbyname("kern.osrelease", str, &size, NULL, 0)) geeft dezelfde informatie als uname, iets gemakkelijker te ontleden.
  4. Gestalt(gestaltSystemVersionMajor)(met zijn “Minor” en BugFix” varianten is de oudste (pre-Carbon!) API die verkrijgbaar is de marketingversie van het besturingssysteem, nog steeds ondersteund door lang verouderd. Beschikbaar in C van het CoreServices-framework, maar niet aanbevolen.
  5. NSAppKitVersionNumberis een float-constante van het AppKit-framework, die de OS-X Appkit-versie (uitgelijnd met de OS-versie) beschikbaar zal stellen voor alle applicaties die linken met AppKit. Het biedt ook een uitgebreide opsomming van alle mogelijke versies (bijv. NSAppKitVersionNumber10_7_2)
  6. kCFCoreFoundationVersionNumberis een float-constante van het CoreFoundation-framework, identiek aan de Appkit-tegenhanger, beschikbaar voor alle apps die zijn gekoppeld aan CoreFoundation, zowel in C, Obj-C als Swift. Het biedt ook een uitgebreide opsomming van alle uitgebrachte versies van OS X (bijv. kCFCoreFoundationVersionNumber10_9)
  7. [[NSProcessInfo processInfo] operatingSystemVersionString];is een Cocoa API die beschikbaar is in Obj-C voor zowel OS-X- als iOS-applicaties.
  8. Er is een resource .plist in /System/Library/CoreServices/SystemVersion.plistdie onder andere de OS-versie in de “ProductVersion”-sleutel bevat. NSProcessInfo leest de informatie uit dit bestand, maar u kunt dit rechtstreeks doen met uw PList-lezende API naar keuze.

Raadpleeg de antwoorden hierboven voor meer informatie over elke optie. Er is daar veel informatie!


Antwoord 10, autoriteit 8%

Er is uname(3):

De functie uname()slaat op nul eindigende reeksen informatie die het huidige systeem identificeert op in de structuur waarnaar wordt verwezen door name.

De utsname-structuur is gedefinieerd in het <sys/utsname.h>headerbestand, en
bevat de volgende leden:

  • sysname– Naam van de implementatie van het besturingssysteem.
  • nodename– Netwerknaam van deze machine.
  • release– Releaseniveau van het besturingssysteem.
  • version– Versieniveau van het besturingssysteem.
  • machine– Machinehardwareplatform.

Antwoord 11, autoriteit 7%

Tijdens het spelen met sysctlop macOS vandaag stuitte ik op kern.osproductversiondie inderdaad de huidige OS-versie bevat. Op mijn Mac met Mojave krijg ik:

$ sysctl kern.osproductversion
kern.osproductversion: 10.14.3

De waarde kan natuurlijk ook programmatisch worden opgehaald:

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

Deze commitaan de xnu-kernel codebase geeft aan dat osproductversioneen recente toevoeging is die medio 2018 binnenkwam. Ik heb het met succes getest op 10.14.3 en 10.13.6.

Al met al zou de beste programmatische, niet-Cocoa-aanpak naar mijn mening zijn om te controleren of kern.osproductversioneen geldig resultaat oplevert en terug te vallen op kern.osreleaseen handmatige toewijzing zoals beschreven in dit antwoordanders.


Antwoord 12, autoriteit 4%

Gestalt()is een pure C API. Het geaccepteerde antwoord suggereert het gebruik van NSProcessInfomaar dat vereist Objective-C-code en kan niet worden gebruikt als pure C om de een of andere reden een vereiste is. Hetzelfde geldt voor NSAppKitVersionNumber, dat sinds 10.13.4 ook niet meer door Apple wordt bijgewerkt. Het gebruik van de constante kCFCoreFoundationVersionNumberwas mogelijk in C-code, maar deze constante is niet meer bijgewerkt sinds 10.11.4. Het lezen van kern.osreleasemet behulp van sysctlis mogelijk in C, maar vereist een toewijzingstabel of vertrouwt op het huidige kernelversieschema en is dus niet betrouwbaar voor toekomstige versies als het kernelversieschema kan veranderen en een toewijzingstabel kan toekomstige versies niet kennen.

De officiële en aanbevolen manier van Apple is om /System/Library/CoreServices/SystemVersion.plistte lezen, aangezien dit ook is waar Gestalt()het versienummer vandaan haalt , dit is ook waar NSProcessInfo()het versienummer vandaan haalt, dit is ook waar de opdrachtregeltool sw_vershet versienummer vandaan haalt en dit is zelfs waar de nieuwe @available(macOS 10.x, ...)compilerfunctie haalt het versienummer van (ik heb diep in het systeem gedoken om hier meer over te weten te komen). Het zou me niet verbazen als zelfs Swift’s #available(macOS 10.x, *)het versienummer daar vandaan zou halen.

Er is gewoon geen eenvoudige C-aanroep om het versienummer vanaf daar te lezen, dus schreef ik de volgende pure C-code die alleen het CoreFoundation-framework vereist dat zelf een puur C-framework is:

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

De reden voor het gebruik van een dispatch_once_fin plaats van dispatch_onceis dat blokken ook niet puur C zijn. De reden om dispatch_onceüberhaupt te gebruiken, is dat het versienummer niet kan veranderen terwijl het systeem draait, dus er is geen reden om het versienummer meer dan één keer te lezen tijdens een enkele app-run. Het lezen ervan is ook niet gegarandeerd faalveilig en te duur om het telkens opnieuw te lezen als uw app het nodig heeft.

Over niet-failveilig gesproken, ook al is het onwaarschijnlijk, het lezen ervan kan natuurlijk mislukken, in welk geval de functie falseretourneert en altijd false. Ik laat het aan jou over om die zaak op een zinvolle manier in je app-code af te handelen, omdat ik niet weet hoe belangrijk het is om het versienummer van je app te kennen. Als het versienummer niet kritisch is, kun je er misschien omheen werken, anders moet je je app op een gebruiksvriendelijke manier laten mislukken.

Opmerking
Als u alleen het versienummer nodig heeft om alternatieve code uit te voeren, afhankelijk van de systeemversie, is er wellicht een beter alternatief voor het zelf opvragen van het versienummer. Zie deze vraagvoor details.


Antwoord 13

Laat in het spel, maar ik kwam hier ook terecht op zoek naar een antwoord.
Voor wat het waard is, misschien is het van nut voor iemand anders;

In het verleden gebruikte ik de opdrachtregelbenadering:

sw_vers

Wat resulteert in:

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

Elke regel kan afzonderlijk worden opgevraagd (let op de camelback-notatie):

sw_vers -productVersion
10.13.6
sw_vers -productName
Mac OS X
sw_vers -buildVersion
17G65

Dat gezegd hebbende, bedankt voor alle andere oplossingen die hier worden vermeld …


Antwoord 14

//my two cent for Swift (Multiplatform)

#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

Antwoord 15

Update voor macOS Catalina en hoger

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

Other episodes