Есть несколько проблем с двумя популярными ответами:
Сравнение строк с использованием NSNumericSearch
иногда приводит к неинтуитивным результатам (от этого страдают все макросы SYSTEM_VERSION_*
):
[@"10.0" compare:@"10" options:NSNumericSearch] // returns NSOrderedDescending instead of NSOrderedSame
FIX : сначала нормализуйте строки, а затем выполните сравнения. Может быть раздражает попытка получить обе строки в одинаковых форматах.
Использование символов версии базового фреймворка невозможно при проверке будущих версий
NSFoundationVersionNumber_iOS_6_1 // does not exist in iOS 5 SDK
FIX : выполните два отдельных теста, чтобы убедиться, что символ существует, и ТОГДА сравните символы. Однако другое здесь:
Символы базовых версий каркаса не являются уникальными для версий iOS. Несколько версий iOS могут иметь одну и ту же версию фреймворка.
9.2 & 9.3 are both 1242.12
8.3 & 8.4 are both 1144.17
FIX : я считаю, что эта проблема неразрешима
Чтобы решить эти проблемы, следующий метод обрабатывает строки номеров версий как числа base-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; измените base
, чтобы отрегулировать этот диапазон) и может поддерживать произвольное количество компонентов (на данный момент 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");
}