Сравнение номеров версий - PullRequest
2 голосов
/ 01 марта 2011

Некоторое время назад я прочитал, что сравнение номеров версий можно выполнить с помощью следующего фрагмента кода:

NSString *vesrion_1 = @"1.2.1";
NSString *version_2 = @"1.2.0";

if ([version_1 compare:version_2 options:NSNumericSearch] == NSOrderedAscending) {
...
}

Кажется, это работает нормально, но у одного пользователя возникла проблема, связанная с, по-видимому, некорректным сравнением номера версии.

У меня вопрос: безопасно ли при любых обстоятельствах использовать эту технику для сравнения номеров версий? Возможно ли, что приведенное выше сравнение приведет к различным результатам на разных машинах?

Ответы [ 8 ]

4 голосов
/ 13 июня 2012

Я разработал VersionComparator на GitHub, который очень легкий и простой в использовании - не такой функциональный, как некоторые другие решения, но его легко подобрать и использовать.

BOOL greater = [VersionComparator isVersion:@"2.0.0" greaterThanVersion:@"1.1.0"];

Он просто сравнивает числа от мажора до сборки - если 2 больше 1, тогда нет необходимости сравнивать дальше.

Его цель - предоставить как можно более простой интерфейсный код (как в примере выше), а также не предоставлять класс, в котором есть наборы и группы вспомогательного кода. Чаще всего это все, что нужно.

3 голосов
/ 18 марта 2013

NSNumericSearch должен делать именно то, что вы хотите.Он обрабатывает все случаи, которые вы ожидаете от номера версии.Для предыдущего проекта я написал несколько тестов для этого:

+ (NSComparisonResult)compareBundleVersion:(NSString *)a withBundleVersion:(NSString *)b
{
    return [a compare:b options:NSNumericSearch];
}

STAssertEquals(NSOrderedSame, [self compareBundleVersion:@"1" withBundleVersion:@"1"], nil);
STAssertEquals(NSOrderedSame, [self compareBundleVersion:@"1.0" withBundleVersion:@"1.0"], nil);
STAssertEquals(NSOrderedSame, [self compareBundleVersion:@"1.1.12" withBundleVersion:@"1.1.12"], nil);

STAssertEquals(NSOrderedAscending, [self compareBundleVersion:@"1" withBundleVersion:@"2"], nil);
STAssertEquals(NSOrderedAscending, [self compareBundleVersion:@"1" withBundleVersion:@"1.1"], nil);
STAssertEquals(NSOrderedAscending, [self compareBundleVersion:@"1.0" withBundleVersion:@"1.1"], nil);
STAssertEquals(NSOrderedAscending, [self compareBundleVersion:@"1.1.12" withBundleVersion:@"1.1.13"], nil);
STAssertEquals(NSOrderedAscending, [self compareBundleVersion:@"1.1.12" withBundleVersion:@"1.2.1"], nil);

STAssertEquals(NSOrderedDescending, [self compareBundleVersion:@"1.1" withBundleVersion:@"1"], nil);
STAssertEquals(NSOrderedDescending, [self compareBundleVersion:@"1.1" withBundleVersion:@"1.0"], nil);
STAssertEquals(NSOrderedDescending, [self compareBundleVersion:@"1.1.13" withBundleVersion:@"1.1.12"], nil);
STAssertEquals(NSOrderedDescending, [self compareBundleVersion:@"1.2.1" withBundleVersion:@"1.1.12"], nil);

Я также проверил это с предыдущими случаями, упомянутыми другим автором:

STAssertEquals(NSOrderedDescending, [self compareBundleVersion:@"1.30" withBundleVersion:@"1.3"], nil);
STAssertEquals(NSOrderedDescending, [self compareBundleVersion:@"1.19" withBundleVersion:@"1.3"], nil);

Все проходит как выбудет ожидать.

3 голосов
/ 02 марта 2011

Я не знаю ничего встроенного , которое будет это делать, но я слышал, что Sparkle framework имеет компаратор версий.

Быстрый просмотриз источника выясняется, что объект SUStandardVersionComparator , кажется, отвечает за него.Он соответствует протоколу <SUVersionComparison> , что означает, что вы можете просто использовать его следующим образом:

NSString *versionA = @"1.2.1";
NSString *versionB = @"1.2.0";
id <SUVersionComparison> comparator = [SUStandardVersionComparator defaultComparator];
NSInteger result = [comparator compareVersion:versionA toVersion:versionB];
if (result == NSOrderedSame) {
  //versionA == versionB
} else if (result == NSOrderedAscending) {
  //versionA < versionB
} else {
  //versionA > versionB
}

(примечание: код не проверен и введен в браузере. ПредупреждениеImplementor )

2 голосов
/ 02 марта 2011

Я бы сказал, нет, это не безопасно.Номера версий на самом деле не числа, а иерархии чисел.Рассмотрим, например, три номера версий:

1.19
1.3
1.30

В результате числового сравнения 1,19 будет меньше 1,3 и 1,30.Было бы также сказать, 1,3 и 1,30 равны.Если выше указаны номера версий, это почти наверняка не то, что вам нужно.

Существует также проблема локализации *.На французском языке вышеприведенное не будет даже обрабатываться как числа.

Гораздо лучше рассматривать номера версий как иерархию отдельных целых чисел.Вы можете легко разделить их с помощью -componentsSeparatedByString:

* По иронии судьбы, мой браузер помечает неправильное написание английского языка как британско-английское.

1 голос
/ 11 июля 2013

Мне нравится ответ Майка (без кода), но он также должен работать, даже если есть вопрос об опциях сравнения, работает NSNumericSearch.

- (NSComparisonResult)compareVersion:(NSString *)versionA to:(NSString *)versionB
{
   NSArray *versionAComp = [versionA componentsSeparatedByString:@"."];
   NSArray *versionBComp = [versionB componentsSeparatedByString:@"."];

   __block NSComparisonResult result = NSOrderedSame;

   [versionAComp enumerateObjectsUsingBlock:
   ^(NSString *obj, NSUInteger idx, BOOL *stop)
   {
        // handle abbreviated versions.
        if (idx > versionBComp.count -1)
        {
            *stop = YES;
            return;
        }

        NSInteger verAInt = [versionAComp[idx] integerValue];
        NSInteger verBInt = [versionBComp[idx] integerValue];

        if (verAInt != verBInt)
        {
            if (verAInt < verBInt)
                result = NSOrderedAscending;
            else
                result = NSOrderedDescending;

            *stop = YES;
            return;
        }
    }];
   return result; 
}
0 голосов
/ 28 февраля 2014

Использование NSNumericSearch работает в большинстве случаев, но не работает, когда форматы версий отличаются;

, например, при сравнении

[self compareBundleVersion:@"1.1.12" withBundleVersion:@"1.2"];

он возвращает NSOrderedDescending, в то время как должен возвращать NSOrderedAscending.

Пожалуйста, взгляните на мою категорию NSString CompareToVersion, которая хорошо справляется с этим;

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

Есть также несколько помощников;

[@"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"];

Проверьте это на;https://github.com/stijnster/NSString-compareToVersion

0 голосов
/ 24 октября 2013

В Java вы можете использовать следующий фрагмент кода для сравнения версий. Если один номер версии короче другого, мы нормализуем его нулями.

import java.util.Arrays;
import java.util.Comparator;

public class FreePlay {

    static String[] versions = {"1.5.3.2", "2.3.4", "1.0.3.4", "10.10.1.1", "1.0.2.5", "2.3.4"};
    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("Unsorted versions");
        for (String s: versions) {
            System.out.print("'" + s + "', ");
        }

        System.out.println("\nSorted versions");

        Arrays.sort(versions, new Comparator<String>() {

            @Override
            public int compare(String o1, String o2) {
                String[] firstVersions = o1.split("\\.");
                String[] secondVersions = o2.split("\\.");
                int length = Math.max(firstVersions.length, secondVersions.length);
                for(int i = 0; i < length; i++) {
                    // normalize the length. If one part is short, we normalize it with zero
                    int firstVersion = i < firstVersions.length ? Integer.parseInt(firstVersions[i]) : 0;
                    int secondVersion = i < secondVersions.length ? Integer.parseInt(secondVersions[i]) : 0;
                    if(firstVersion < secondVersion)
                        return -1;
                    if(firstVersion > secondVersion)
                        return 1;
                }
                return 0;
            }});

        for (String s: versions) {
            System.out.print("'" + s + "', ");
        }
    }

}
0 голосов
/ 12 июля 2013

Из документации Apple:

if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
    // Load resources for iOS 6.1 or earlier
} 
else {
    // Load resources for iOS 7 or later
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...