在Objective-C中比较版本号

91
我正在编写一个应用程序,该应用程序接收包含项目和版本号的数据。这些数字的格式为“1.0.1”或“1.2.5”。 我如何比较这些版本号? 我认为它们必须首先格式化为字符串,对吗?我有哪些选项可以确定“1.2.5”在“1.0.1”之后?

我编写了一个小库,可以轻松比较Obj-C中的2个版本字符串,通常在iOS中使用。在GitHub页面上有例子和代码。 - nembleton
3
有助于明确版本控制方案。有些可能需要额外的逻辑格式。 - uchuugaka
15个回答

249

这是比较版本号最简单的方法,需要记住 "1" < "1.0" < "1.0.0":

NSString* requiredVersion = @"1.2.0";
NSString* actualVersion = @"1.1.5";

if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
  // actualVersion is lower than the requiredVersion
}

6
我一直在使用这种方法,最近发现当比较2.4.06和2.4.4时,它返回了(我认为)错误的结果。我认为2.4.06应该比2.4.4低,但也许我错了...你有什么想法吗? - Omer
9
为什么要写成06而不是6呢?我认为大多数开发人员会认为版本号2.4.06比2.4.4更高。 - Stephen Melvin
4
这很简单易懂,但依赖于一个非常简单的版本方案。 - uchuugaka
11
@ScottBerrevoets 我当然希望它不是这样工作的,因为这意味着“1.2.3”小于“1.1.12”(123 < 1112)!正如苹果小心地指出的那样,“字符串中的数字将使用数值进行比较”。也就是说,字符串中的每组数字都将被比较(基本上是采用componentsSeparatedByString方法)。你可以用@"1.8"@"1.7.2.3.55"进行测试,看看1.8是否领先。 - dooleyo
4
NSNumericSearch认为"1.0"小于"1.0.0"。这对我的需求来说还不够灵活。 - bobics
显示剩余11条评论

18

我将添加我的方法,该方法将严格比较只包含数字版本(没有a、b、RC等)以及任意数量的组件。

+ (NSComparisonResult)compareVersion:(NSString*)versionOne toVersion:(NSString*)versionTwo {
    NSArray* versionOneComp = [versionOne componentsSeparatedByString:@"."];
    NSArray* versionTwoComp = [versionTwo componentsSeparatedByString:@"."];

    NSInteger pos = 0;

    while ([versionOneComp count] > pos || [versionTwoComp count] > pos) {
        NSInteger v1 = [versionOneComp count] > pos ? [[versionOneComp objectAtIndex:pos] integerValue] : 0;
        NSInteger v2 = [versionTwoComp count] > pos ? [[versionTwoComp objectAtIndex:pos] integerValue] : 0;
        if (v1 < v2) {
            return NSOrderedAscending;
        }
        else if (v1 > v2) {
            return NSOrderedDescending;
        }
        pos++;
    }

    return NSOrderedSame;
}

14

这是对Nathan de Vries的回答进行扩展,以解决1 < 1.0 < 1.0.0等问题。

首先,我们可以通过一个NSString类别来解决版本字符串中额外的".0"问题:

@implementation NSString (VersionNumbers)
- (NSString *)shortenedVersionNumberString {
    static NSString *const unnecessaryVersionSuffix = @".0";
    NSString *shortenedVersionNumber = self;

    while ([shortenedVersionNumber hasSuffix:unnecessaryVersionSuffix]) {
        shortenedVersionNumber = [shortenedVersionNumber substringToIndex:shortenedVersionNumber.length - unnecessaryVersionSuffix.length];
    }

    return shortenedVersionNumber;
}
@end

通过上述的NSString类别,我们可以缩短版本号以删除不必要的.0

NSString* requiredVersion = @"1.2.0";
NSString* actualVersion = @"1.1.5";

requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2
actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5

现在我们仍然可以使用Nathan de Vries提出的简单而美丽的方法:

if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
  // actualVersion is lower than the requiredVersion
}

这个答案与Nathan de Vries的解决方案相结合,是最好、最优雅的答案。 - Dalmazio
这是否意味着7.4.2仍然比7.5更高版本? - Tres
@Tres 不是。因为 NSNumericSearch 被传递为选项,所以字符串被视为数字进行比较,因此 7.4.2 < 7.5。 - DonnaLea

10

我自己做的,使用了分类...

来源...

@implementation NSString (VersionComparison)
- (NSComparisonResult)compareVersion:(NSString *)version{
    NSArray *version1 = [self componentsSeparatedByString:@"."];
    NSArray *version2 = [version componentsSeparatedByString:@"."];
    for(int i = 0 ; i < version1.count || i < version2.count; i++){
        NSInteger value1 = 0;
        NSInteger value2 = 0;
        if(i < version1.count){
            value1 = [version1[i] integerValue];
        }
        if(i < version2.count){
            value2 = [version2[i] integerValue];
        }
        if(value1  == value2){
            continue;
        }else{
            if(value1 > value2){
                return NSOrderedDescending;
            }else{
                return NSOrderedAscending;
            }
        }
    }
    return NSOrderedSame;
}

测试..

NSString *version1 = @"3.3.1";
NSString *version2 = @"3.12.1";
NSComparisonResult result = [version1 compareVersion:version2];
switch (result) {
    case NSOrderedAscending:
    case NSOrderedDescending:
    case NSOrderedSame:
         break;
    }

太棒了!这是该线程上唯一一个使用NSComparisonResult正确比较7.28.2和7.28的示例。 - CokePokes

8

Sparkle 是MacOS最受欢迎的软件更新框架,它拥有一个SUStandardVersionComparator类来完成这项任务,而且还考虑了构建号和测试版标识。例如:它可以正确比较1.0.5 > 1.0.5b72.0 (2345) > 2.0 (2100)。该代码仅使用Foundation框架,因此在iOS上也可以正常工作。


7

请查看我的NSString类别,它在github上实现了易于版本检查的功能;https://github.com/stijnster/NSString-compareToVersion

[@"1.2.2.4" compareToVersion:@"1.2.2.5"];

这将返回一个比使用其他方法更准确的NSComparisonResult
[@"1.2.2" compare:@"1.2.2.5" options:NSNumericSearch]

助手们也被添加了;
[@"1.2.2.4" isOlderThanVersion:@"1.2.2.5"];
[@"1.2.2.4" isNewerThanVersion:@"1.2.2.5"];
[@"1.2.2.4" isEqualToVersion:@"1.2.2.5"];
[@"1.2.2.4" isEqualOrOlderThanVersion:@"1.2.2.5"];
[@"1.2.2.4" isEqualOrNewerThanVersion:@"1.2.2.5"];

4

Swift 2.2 版本:

let currentStoreAppVersion = "1.10.2"
let minimumAppVersionRequired = "1.2.2"
if currentStoreAppVersion.compare(minimumAppVersionRequired, options: NSStringCompareOptions.NumericSearch) ==
            NSComparisonResult.OrderedDescending {
            print("Current Store version is higher")
        } else {
            print("Latest New version is higher")
        }

Swift 3版本:

let currentStoreVersion = "1.1.0.2"
let latestMinimumAppVersionRequired = "1.1.1"
if currentStoreVersion.compare(latestMinimumAppVersionRequired, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
print("Current version is higher")
} else {
print("Latest version is higher")
}

3
我想分享一个用于检查版本号的功能。它不是完美的,但请看一下示例和结果。如果你需要检查自己的版本号(比如数据库迁移),那么这可能会有所帮助。
(当然,要在方法中删除日志语句。那些只是用来帮助你看到它的作用)
测试结果:
[self isVersion:@"1.0" higherThan:@"0.1"];
[self isVersion:@"1.0" higherThan:@"0.9.5"];
[self isVersion:@"1.0" higherThan:@"0.9.5.1"];
[self isVersion:@"1.0.1" higherThan:@"1.0"];
[self isVersion:@"1.0.0" higherThan:@"1.0.1"];
[self isVersion:@"1.0.0" higherThan:@"1.0.0"];

// alpha tests
[self isVersion:@"1.0b" higherThan:@"1.0a"];
[self isVersion:@"1.0a" higherThan:@"1.0b"];
[self isVersion:@"1.0a" higherThan:@"1.0a"];
[self isVersion:@"1.0" higherThan:@"1.0RC1"];
[self isVersion:@"1.0.1" higherThan:@"1.0RC1"];

结果:

1.0 > 0.1
1.0 > 0.9.5
1.0 > 0.9.5.1
1.0.1 > 1.0
1.0.0 < 1.0.1
1.0.0 == 1.0.0
1.0b > 1.0a
1.0a < 1.0b
1.0a == 1.0a
1.0 < 1.0RC1       <-- FAILURE
1.0.1 < 1.0RC1     <-- FAILURE

请注意,alpha可以使用,但必须非常小心。一旦您在某个地方使用alpha,您就不能通过更改其后面的任何其他次要数字来扩展它。

代码:

- (BOOL) isVersion:(NSString *)thisVersionString higherThan:(NSString *)thatVersionString {

// LOWER
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending) {
    NSLog(@"%@ < %@", thisVersionString, thatVersionString);
    return NO;
}

// EQUAL
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedSame) {
    NSLog(@"%@ == %@", thisVersionString, thatVersionString);
    return NO;
}

NSLog(@"%@ > %@", thisVersionString, thatVersionString);
// HIGHER
return YES;
}

3
我的iOS库AppUpdateTracker包含一个NSString类别,用于执行此类比较。(实现基于DonnaLea的答案。)使用方法如下:
[@"1.4" isGreaterThanVersionString:@"1.3"]; // YES
[@"1.4" isLessThanOrEqualToVersionString:@"1.3"]; // NO

此外,您可以使用它来跟踪您的应用程序的安装/更新状态:
[AppUpdateTracker registerForAppUpdatesWithBlock:^(NSString *previousVersion, NSString *currentVersion) {
    NSLog(@"app updated from: %@ to: %@", previousVersion, currentVersion);
}];
[AppUpdateTracker registerForFirstInstallWithBlock:^(NSTimeInterval installTimeSinceEpoch, NSUInteger installCount) {
    NSLog(@"first install detected at: %f amount of times app was (re)installed: %lu", installTimeSinceEpoch, (unsigned long)installCount);
}];
[AppUpdateTracker registerForIncrementedUseCountWithBlock:^(NSUInteger useCount) {
    NSLog(@"incremented use count to: %lu", (unsigned long)useCount);
}];

这段代码的意思是:v4.21 < 4.3 吗? 如果([thisVersion isGreaterThanOrEqualToVersionString:@"4.3"]) - johndpope
不,4.21被认为比4.3大,因为21>3。为了满足您的相等比较,您需要将4.21与4.30进行比较。请参见Nathan de Vries的答案中的评论讨论。 - Stunner

3
以下是版本比较的 Swift 4.0+ 代码:

以下是版本比较的 Swift 4.0+ 代码:

 let currentVersion = "1.2.0"

 let oldVersion = "1.1.1"

 if currentVersion.compare(oldVersion, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
        print("Higher")
    } else {
        print("Lower")
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接