Bandingkan nomor versi di Objective-C


87

Saya menulis aplikasi yang menerima data dengan item dan nomor versi. Angka-angka tersebut memiliki format seperti "1.0.1" atau "1.2.5". Bagaimana cara membandingkan nomor versi ini? Saya pikir mereka harus diformat sebagai string terlebih dahulu, bukan? Opsi apa yang saya miliki untuk menentukan bahwa "1.2.5" muncul setelah "1.0.1"?


Saya menulis perpustakaan kecil itu untuk dengan mudah membandingkan 2 versi Strings di Obj-C. Biasanya di iOS. Miliki contoh dan kode pada halaman GitHub
nembleton

3
Ini membantu untuk memperjelas dengan tepat apa skema pembuatan versi. Beberapa mungkin memiliki format yang membutuhkan logika tambahan.
uchuugaka

Jawaban:


244

Ini adalah cara termudah untuk membandingkan versi, dengan mengingat bahwa "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
Saya menggunakan metode ini, dan saya baru-baru ini menemukan bahwa ia mengembalikan (apa yang saya anggap) hasil yang salah saat membandingkan yaitu: 2.4.06 dengan 2.4.4. Saya percaya bahwa 2.4.06 harus lebih rendah dari 2.4.4, tetapi mungkin saya salah ... ada pemikiran?
Omer

7
@Omer: Mengapa 06 dan bukan 6? Saya pikir sebagian besar pengembang akan menganggap 2.4.06 sebagai versi yang lebih tinggi dari 2.4.4.
Stephen Melvin

4
Ini bagus dan sederhana tetapi bergantung pada skema versi yang sangat sederhana.
uchuugaka

11
@ScottBerrevoets Saya pasti berharap bukan itu cara kerjanya, karena itu berarti "1.2.3" kurang dari "1.1.12" (123 <1112)! Seperti yang dinyatakan Apple dengan cermat, " Angka dalam string dibandingkan menggunakan nilai numerik ". Artinya, kumpulan angka dalam string masing-masing akan dibandingkan (pada dasarnya, componentsSeparatedByStringpendekatan). Anda dapat menguji ini sendiri dengan @"1.8"vs @"1.7.2.3.55"dan melihat bahwa 1,8 keluar lebih dulu.
dooleyo

3
NSNumericSearch menganggap "1.0" lebih kecil dari "1.0.0". Tidak cukup fleksibel untuk tujuan saya.
bobics

17

Saya akan menambahkan metode saya, yang membandingkan secara ketat versi numerik (no a, b, RC dll.) Dengan sejumlah komponen.

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

13

Ini adalah perluasan jawaban Nathan de Vries untuk mengatasi masalah 1 <1.0 <1.0.0 dll.

Pertama kita bisa mengatasi masalah ekstra ".0" pada string versi kita dengan NSStringkategori:

@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

Dengan NSStringkategori di atas kita dapat mempersingkat nomor versi kita untuk menghilangkan .0 yang tidak perlu

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

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

Sekarang kita masih dapat menggunakan pendekatan sederhana yang indah yang diusulkan oleh Nathan de Vries:

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

Ini digabungkan dengan solusi Nathan de Vries adalah jawaban terbaik dan paling elegan.
Dalmazio

Bukankah ini masih mengatakan bahwa 7.4.2 adalah versi yang lebih tinggi dari 7.5?
Tres

@Tres no. Karena NSNumericSearch diteruskan sebagai opsi, string dibandingkan sebagai angka, sehingga 7.4.2 <7.5
DonnaLea

9

Saya membuatnya sendiri, menggunakan Kategori ..

Sumber..

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

Uji..

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

Hebat! Ini adalah satu-satunya contoh yang menggunakan NSComparisonResult di utas ini yang membandingkan 7.28.2 & 7.28 dengan benar.
CokePokes

8

Sparkle (kerangka pembaruan perangkat lunak paling populer untuk MacOS) memiliki kelas SUStandardVersionComparator yang melakukan ini, dan juga memperhitungkan nomor build dan penanda beta. Yaitu dengan benar membandingkan 1.0.5 > 1.0.5b7atau 2.0 (2345) > 2.0 (2100). Kode hanya menggunakan Foundation, jadi harus berfungsi dengan baik di iOS juga.


6

Lihat kategori NSString saya yang menerapkan pemeriksaan versi mudah di github; https://github.com/stijnster/NSString-compareToVersion

Ini akan mengembalikan NSComparisonResult yang lebih akurat daripada menggunakan;

Pembantu juga ditambahkan;


4

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

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

4

Berikut adalah kode 4.0 + cepat untuk perbandingan versi

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

3

Saya pikir saya hanya akan berbagi fungsi yang saya kumpulkan untuk ini. Itu sama sekali tidak sempurna. Silakan lihat contoh dan hasilnya. Tetapi jika Anda memeriksa nomor versi Anda sendiri (yang harus saya lakukan untuk mengelola hal-hal seperti migrasi database) maka ini mungkin sedikit membantu.

(juga, hapus pernyataan log dalam metode, tentu saja. itu ada untuk membantu Anda melihat apa yang dilakukannya itu semua)

Tes:

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

Hasil:

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

perhatikan bahwa alfa berfungsi tetapi Anda harus sangat berhati-hati dengannya. sekali Anda menjadi alfa di beberapa titik, Anda tidak dapat memperpanjangnya dengan mengubah nomor kecil lainnya di belakangnya.

Kode:

- (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

Perpustakaan iOS saya AppUpdateTracker berisi kategori NSString untuk melakukan perbandingan semacam ini. (Implementasi didasarkan pada jawaban DonnaLea .)

Penggunaannya adalah sebagai berikut:

[@"1.4" isGreaterThanVersionString:@"1.3"]; // YES
[@"1.4" isLessThanOrEqualToVersionString:@"1.3"]; // NO

Selain itu, Anda dapat menggunakannya untuk melacak status penginstalan / pembaruan aplikasi Anda:

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

apakah v4.21 <4.3? if ([thisVersion isGreaterThanOrEqualToVersionString: @ "4.3"])
johndpope

Tidak, 4,21 dianggap lebih besar dari 4,3 karena 21> 3. Untuk memenuhi perbandingan kesetaraan Anda, Anda ingin membandingkan 4,21 dengan 4,30. Silahkan simak pembahasannya di kolom komentar jawaban Nathan de Vries .
Cantik

0

Glibc memiliki fungsi strverscmpdan versionsort… sayangnya, tidak portabel untuk iPhone, tetapi Anda dapat menulis sendiri dengan cukup mudah. Implementasi ulang (belum teruji) ini berasal dari hanya membaca perilaku yang didokumentasikan, dan bukan dari membaca kode sumber Glibc.

int strverscmp(const char *s1, const char *s2) {
    const char *b1 = s1, *b2 = s2, *e1, *e2;
    long n1, n2;
    size_t z1, z2;
    while (*b1 && *b1 == *b2) b1++, b2++;
    if (!*b1 && !*b2) return 0;
    e1 = b1, e2 = b2;
    while (b1 > s1 && isdigit(b1[-1])) b1--;
    while (b2 > s2 && isdigit(b2[-1])) b2--;
    n1 = strtol(b1, &e1, 10);
    n2 = strtol(b2, &e2, 10);
    if (b1 == e1 || b2 == e2) return strcmp(s1, s2);
    if (n1 < n2) return -1;
    if (n1 > n2) return 1;
    z1 = strspn(b1, "0"), z2 = strspn(b2, "0");
    if (z1 > z2) return -1;
    if (z1 < z2) return 1;
    return 0;
}

2
ini terlihat mengerikan. Salah satu hal yang paling saya suka tentang Objective-C adalah bahwa sebagian besar saya tidak harus berurusan dengan C biasa lagi.
Lukas Petr

0

Jika Anda tahu bahwa setiap nomor versi akan memiliki tepat 3 bilangan bulat yang dipisahkan oleh titik, Anda dapat menguraikannya (misalnya menggunakan sscanf(3)) dan membandingkannya:

const char *version1str = "1.0.1";
const char *version2str = "1.2.5";
int major1, minor1, patch1;
int major2, minor2, patch2;
if(sscanf(version1str, "%d.%d.%d", &major1, &minor1, &patch1) == 3 &&
   sscanf(version2str, "%d.%d.%d", &major2, &minor2, &patch2) == 3)
{
    // Parsing succeeded, now compare the integers
    if(major1 > major2 ||
      (major1 == major2 && (minor1 > minor2 ||
                           (minor1 == minor2 && patch1 > patch2))))
    {
        // version1 > version2
    }
    else if(major1 == major2 && minor1 == minor2 && patch1 == patch2)
    {
        // version1 == version2
    }
    else
    {
        // version1 < version2
    }
}
else
{
    // Handle error, parsing failed
}

0

Untuk memeriksa versi dengan cepat Anda dapat menggunakan berikut ini

switch newVersion.compare(currentversion, options: NSStringCompareOptions.NumericSearch) {
    case .OrderedDescending:
        println("NewVersion available  ")
        // Show Alert Here

    case .OrderedAscending:
        println("NewVersion Not available  ")
    default:
        println("default")
    }

Semoga bermanfaat.


0

Berikut adalah fungsi rekursif yang bekerja dengan beberapa versi format dengan panjang berapa pun. Ini juga berfungsi untuk @ "1.0" dan @ "1.0.0"

static inline NSComparisonResult versioncmp(const NSString * a, const NSString * b)
{
    if ([a isEqualToString:@""] && [b isEqualToString:@""]) {
        return NSOrderedSame;
    }

    if ([a isEqualToString:@""]) {
        a = @"0";
    }

    if ([b isEqualToString:@""]) {
        b = @"0";
    }

    NSArray<NSString*> * aComponents = [a componentsSeparatedByString:@"."];
    NSArray<NSString*> * bComponents = [b componentsSeparatedByString:@"."];
    NSComparisonResult r = [aComponents[0] compare:bComponents[0] options:NSNumericSearch];

    if(r != NSOrderedSame) {
        return r;
    } else {
        NSString* newA = (a.length == aComponents[0].length) ? @"" : [a substringFromIndex:aComponents[0].length+1];
        NSString* newB = (b.length == bComponents[0].length) ? @"" : [b substringFromIndex:bComponents[0].length+1];
        return versioncmp(newA, newB);
    }

}

Sampel uji:

versioncmp(@"11.5", @"8.2.3");
versioncmp(@"1.5", @"8.2.3");
versioncmp(@"1.0", @"1.0.0");
versioncmp(@"11.5.3.4.1.2", @"11.5.3.4.1.2");
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.