我想检查设备的iOS版本是否大于3.1.3 我尝试了以下方法:

[[UIDevice currentDevice].systemVersion floatValue]

但是不管用,我只想要一个:

if (version > 3.1.3) { }

我怎样才能做到这一点呢?


当前回答

UIDevice + IOSVersion.h

@interface UIDevice (IOSVersion)

+ (BOOL)isCurrentIOSVersionEqualToVersion:(NSString *)iOSVersion;
+ (BOOL)isCurrentIOSVersionGreaterThanVersion:(NSString *)iOSVersion;
+ (BOOL)isCurrentIOSVersionGreaterThanOrEqualToVersion:(NSString *)iOSVersion;
+ (BOOL)isCurrentIOSVersionLessThanVersion:(NSString *)iOSVersion;
+ (BOOL)isCurrentIOSVersionLessThanOrEqualToVersion:(NSString *)iOSVersion

@end

UIDevice + IOSVersion.m

#import "UIDevice+IOSVersion.h"

@implementation UIDevice (IOSVersion)

+ (BOOL)isCurrentIOSVersionEqualToVersion:(NSString *)iOSVersion
{
    return [[[UIDevice currentDevice] systemVersion] compare:iOSVersion options:NSNumericSearch] == NSOrderedSame;
}

+ (BOOL)isCurrentIOSVersionGreaterThanVersion:(NSString *)iOSVersion
{
    return [[[UIDevice currentDevice] systemVersion] compare:iOSVersion options:NSNumericSearch] == NSOrderedDescending;
}

+ (BOOL)isCurrentIOSVersionGreaterThanOrEqualToVersion:(NSString *)iOSVersion
{
    return [[[UIDevice currentDevice] systemVersion] compare:iOSVersion options:NSNumericSearch] != NSOrderedAscending;
}

+ (BOOL)isCurrentIOSVersionLessThanVersion:(NSString *)iOSVersion
{
    return [[[UIDevice currentDevice] systemVersion] compare:iOSVersion options:NSNumericSearch] == NSOrderedAscending;
}

+ (BOOL)isCurrentIOSVersionLessThanOrEqualToVersion:(NSString *)iOSVersion
{
    return [[[UIDevice currentDevice] systemVersion] compare:iOSVersion options:NSNumericSearch] != NSOrderedDescending;
}

@end

其他回答

一般来说,最好是询问一个对象是否可以执行给定的选择器,而不是检查版本号来决定它是否必须存在。

当这不是一个选项,你需要在这里有点小心,因为[@"5.0"比较:@"5"选项:NSNumericSearch]返回nsordereddescent这可能不是有意的;我可能期待NSOrderedSame。这至少是一个理论上的问题,在我看来,这是一个值得防范的问题。

同样值得考虑的是,可能会出现无法合理比较的糟糕版本输入。苹果提供了三个预定义的常量NSOrderedAscending, NSOrderedSame和nsordereddescent,但我可以想到一个叫做NSOrderedUnordered的东西的使用,在我不能比较两个东西的情况下,我想返回一个值来指示这个。

更重要的是,苹果有一天会扩展他们的三个预定义常量以允许各种返回值,这是不可能的,这使得比较不明智。

考虑下面的代码。

typedef enum {kSKOrderedNotOrdered = -2, kSKOrderedAscending = -1, kSKOrderedSame = 0, kSKOrderedDescending = 1} SKComparisonResult;

@interface SKComparator : NSObject
+ (SKComparisonResult)comparePointSeparatedVersionNumber:(NSString *)vOne withPointSeparatedVersionNumber:(NSString *)vTwo;
@end

@implementation SKComparator
+ (SKComparisonResult)comparePointSeparatedVersionNumber:(NSString *)vOne withPointSeparatedVersionNumber:(NSString *)vTwo {
  if (!vOne || !vTwo || [vOne length] < 1 || [vTwo length] < 1 || [vOne rangeOfString:@".."].location != NSNotFound ||
    [vTwo rangeOfString:@".."].location != NSNotFound) {
    return SKOrderedNotOrdered;
  }
  NSCharacterSet *numericalCharSet = [NSCharacterSet characterSetWithCharactersInString:@".0123456789"];
  NSString *vOneTrimmed = [vOne stringByTrimmingCharactersInSet:numericalCharSet];
  NSString *vTwoTrimmed = [vTwo stringByTrimmingCharactersInSet:numericalCharSet];
  if ([vOneTrimmed length] > 0 || [vTwoTrimmed length] > 0) {
    return SKOrderedNotOrdered;
  }
  NSArray *vOneArray = [vOne componentsSeparatedByString:@"."];
  NSArray *vTwoArray = [vTwo componentsSeparatedByString:@"."];
  for (NSUInteger i = 0; i < MIN([vOneArray count], [vTwoArray count]); i++) {
    NSInteger vOneInt = [[vOneArray objectAtIndex:i] intValue];
    NSInteger vTwoInt = [[vTwoArray objectAtIndex:i] intValue];
    if (vOneInt > vTwoInt) {
      return kSKOrderedDescending;
    } else if (vOneInt < vTwoInt) {
      return kSKOrderedAscending;
    }
  }
  if ([vOneArray count] > [vTwoArray count]) {
    for (NSUInteger i = [vTwoArray count]; i < [vOneArray count]; i++) {
      if ([[vOneArray objectAtIndex:i] intValue] > 0) {
        return kSKOrderedDescending;
      }
    }
  } else if ([vOneArray count] < [vTwoArray count]) {
    for (NSUInteger i = [vOneArray count]; i < [vTwoArray count]; i++) {
      if ([[vTwoArray objectAtIndex:i] intValue] > 0) {
        return kSKOrderedAscending;
      }
    }
  }
  return kSKOrderedSame;
}
@end

这两种常见的答案存在一些问题:

Comparing strings using NSNumericSearch sometimes has unintuitive results (the SYSTEM_VERSION_* macros all suffer from this): [@"10.0" compare:@"10" options:NSNumericSearch] // returns NSOrderedDescending instead of NSOrderedSame FIX: Normalize your strings first and then perform the comparisons. could be annoying trying to get both strings in identical formats. Using the foundation framework version symbols is not possible when checking future releases NSFoundationVersionNumber_iOS_6_1 // does not exist in iOS 5 SDK FIX: Perform two separate tests to ensure the symbol exists AND THEN compare symbols. However another here: The foundation framwork versions symbols are not unique to iOS versions. Multiple iOS releases can have the same framework version. 9.2 & 9.3 are both 1242.12 8.3 & 8.4 are both 1144.17 FIX: I believe this issue is unresolvable


为了解决这些问题,下面的方法将版本号字符串处理为以10000为基数的数字(每个主要/次要/补丁组件都是一个单独的数字),并执行以十进制为基数的转换,以便使用整数运算符进行比较。

添加了另外两个方法,用于方便地比较iOS版本字符串,以及比较字符串与任意数量的组件。

+ (SInt64)integerFromVersionString:(NSString *)versionString withComponentCount:(NSUInteger)componentCount
{
    //
    // performs base conversion from a version string to a decimal value. the version string is interpreted as
    // a base-10000 number, where each component is an individual digit. this makes it simple to use integer
    // operations for comparing versions. for example (with componentCount = 4):
    //
    //   version "5.9.22.1" = 5*1000^3 + 9*1000^2 + 22*1000^1 + 1*1000^0 = 5000900220001
    //    and
    //   version "6.0.0.0" = 6*1000^3 + 0*1000^2 + 0*1000^1 + 0*1000^1 = 6000000000000
    //    and
    //   version "6" = 6*1000^3 + 0*1000^2 + 0*1000^1 + 0*1000^1 = 6000000000000
    //
    // then the integer comparisons hold true as you would expect:
    //
    //   "5.9.22.1" < "6.0.0.0" // true
    //   "6.0.0.0" == "6"       // true
    //

    static NSCharacterSet *nonDecimalDigitCharacter;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,
        ^{  // don't allocate this charset every time the function is called
            nonDecimalDigitCharacter = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
        });

    SInt64 base    = 10000; // each component in the version string must be less than base
    SInt64 result  =     0;
    SInt64 power   =     0;

    // construct the decimal value left-to-right from the version string
    for (NSString *component in [versionString componentsSeparatedByString:@"."])
    {
        if (NSNotFound != [component rangeOfCharacterFromSet:nonDecimalDigitCharacter].location)
        {
            // one of the version components is not an integer, so bail out
            result = -1;
            break;
        }
        result += [component longLongValue] * (long long)pow((double)base, (double)(componentCount - ++power));
    }

    return result;
}

+ (SInt64)integerFromVersionString:(NSString *)versionString
{
    return [[self class] integerFromVersionString:versionString
                               withComponentCount:[[versionString componentsSeparatedByString:@"."] count]];
}

+ (SInt64)integerFromiOSVersionString:(NSString *)versionString
{
    // iOS uses 3-component version string
    return [[self class] integerFromVersionString:versionString
                               withComponentCount:3];
}

它支持许多版本标识符(通过4位数字,0-9999;可以支持任意数量的组件(Apple目前似乎使用3个组件,例如major.minor.patch),但这可以使用componentCount参数显式指定。确保你的componentCount和base不会导致溢出,即确保2^63 >= base^componentCount!

使用的例子:

NSString *currentVersion = [[UIDevice currentDevice] systemVersion];
if ([Util integerFromiOSVersionString:currentVersion] >= [Util integerFromiOSVersionString:@"42"])
{
    NSLog(@"we are in some horrible distant future where iOS still exists");
}

基本上和这个想法一样https://stackoverflow.com/a/19903595/1937908但更健壮:

#ifndef func_i_system_version_field
#define func_i_system_version_field

inline static int i_system_version_field(unsigned int fieldIndex) {
  NSString* const versionString = UIDevice.currentDevice.systemVersion;
  NSArray<NSString*>* const versionFields = [versionString componentsSeparatedByString:@"."];
  if (fieldIndex < versionFields.count) {
    NSString* const field = versionFields[fieldIndex];
    return field.intValue;
  }
  NSLog(@"[WARNING] i_system_version(%iu): field index not present in version string '%@'.", fieldIndex, versionString);
  return -1; // error indicator
}

#endif

只需将上面的代码放在一个头文件中。

用法:

int major = i_system_version_field(0);
int minor = i_system_version_field(1);
int patch = i_system_version_field(2);
/*
 *  System Versioning Preprocessor Macros
 */ 

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

/*
 *  Usage
 */ 

if (SYSTEM_VERSION_LESS_THAN(@"4.0")) {
    ...
}

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"3.1.1")) {
    ...
}

我总是把它们保存在Constants.h文件中:

#define IS_IPHONE5 (([[UIScreen mainScreen] bounds].size.height-568)?NO:YES) 
#define IS_OS_5_OR_LATER    ([[[UIDevice currentDevice] systemVersion] floatValue] >= 5.0)
#define IS_OS_6_OR_LATER    ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0)
#define IS_OS_7_OR_LATER    ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)
#define IS_OS_8_OR_LATER    ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)