傳送門:排序算法演示小DEMOios
爲了給字符串數組排序,除了用C/C++的基本辦法,iOS開發者更應該學會利用蘋果專門爲NSArray 排序提供的sortedArrayUsingComparator 方法:git
- (NSArray<ObjectType> *)sortedArrayUsingComparator:(NSComparator NS_NOESCAPE)cmptr NS_AVAILABLE(10_6, 4_0);
複製代碼
其中,須要設置一個NSComparator 參數,它是一個block,查看定義以下:github
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
複製代碼
這個block體返回的NSComparisonResult 是一個枚舉類型,它的定義是:算法
typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};
複製代碼
問題來了,怎麼設置?express
- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToCompare locale:(nullable id)locale; // locale arg used to be a dictionary pre-Leopard. We now accept NSLocale. Assumes the current locale if non-nil and non-NSLocale. nil continues to mean canonical compare, which doesn't depend on user's locale choice.
複製代碼
這時候,就須要瞭解NSStringCompareOptions 的意思。但若是你搜索一下NSStringCompareOptions ,會發現不少文章中的翻譯或者中文解釋在誤導,或者很難看清什麼意思?例以下面這篇博客:macos
而後,相同的解釋文案還以訛傳訛的傳開來了,例如你看下面這個博客:數組
因而,筆者決定寫此本文,好好展現他們的用途。bash
void handleSortingForIntStrArray(void){
NSArray *originalArray = @[@"00",@"0",@"00",@"01",@"10",@"21",@"12",@"11",@"22"];
//block比較方法,數組中能夠是NSInteger,NSString(須要轉換)
NSComparator finderSort = ^(id string1,id string2){
if ([string1 integerValue] > [string2 integerValue]) {
return (NSComparisonResult)NSOrderedDescending;
}else if ([string1 integerValue] < [string2 integerValue]){
return (NSComparisonResult)NSOrderedAscending;
}else{
return (NSComparisonResult)NSOrderedSame;
}
};
//數組排序:
NSArray *resultArray = [originalArray sortedArrayUsingComparator:finderSort];
NSLog(@"第一種排序結果:%@",resultArray);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Results of handleSortingForIntArray()**********************");
handleSortingForIntStrArray();
}
return 0;
}
複製代碼
上面的代碼中用到了NSComparator與NSComparisonResult,在本文的「前面的話」中已經介紹過,這裏從新列一下定義。app
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
複製代碼
typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};
複製代碼
//
// main.m
// SortingForArray
//
// Created by ChenMan on 2017/12/20.
// Copyright © 2017年 ChenMan. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <stdio.h>
void handleSortingForStrArray(void){
NSArray *stringsArray = [NSArray arrayWithObjects:
@"string b",
@"string A",
@"string a",
@"string \uFF41",
@"string a",
@"string A",
@"string c",
@"string d0030",
@"string d2",
@"アいろはアイウエイウエ",
@"アいろはアイウエイウエ",
@"アいろはアイウエイウエ",nil];
NSLocale *currentLocale = [NSLocale currentLocale];
NSComparator finderSortBlock = ^(id string1,id string2) {
NSRange string1Range =NSMakeRange(0, [string1 length]);
return [string1 compare:string2 options:nil range:string1Range locale:currentLocale];
};
NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];
NSLog(@"finderSortArray: %@", finderSortArray);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Results of handleSortingForStrArray()**********************");
handleSortingForStrArray();
}
return 0;
}
複製代碼
如上實驗代碼中,有這樣一行代碼:工具
return [string1 compare:string2 options:nil range:string1Range locale:currentLocale];
複製代碼
根據運行結果,可知以下結論:
- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToCompare locale:(nullable id)locale;
中將(NSStringCompareOptions)
枚舉類型的參數設置爲nil
,也能夠運行。但通常不這麼作,這裏只是爲了觀察不指定該枚舉參數時候系統的默認設置,並與本文接下來指定該枚舉參數的排序結果對比。全角a
的unicode爲FF41,半角a
的unicode爲0061,半角A
的unicode爲0041,半角b
的unicode爲0062,但排序結果是 全角a
= 半角a
< 半角A
< 半角b
。半角與全角字符
全角佔兩個字節,半角佔一個字節。一般咱們碰到的英文字母、數字鍵、符號鍵這種ASCII碼系統裏面的字符大多數狀況下是半角的。
國內漢字輸入法輸入的漢字爲全角,字母數字爲半角,可是標點則默認爲全角,可切換爲半角(能夠經過輸入法工具條上的相應按鈕來切換標點符號的全角半角狀態)。
日文裏面的有漢字,也有片假字。這個片假字有兩套編碼,同一個片假字分別有半角和全角兩種編碼。例如:看起來像同樣的片假字組成的句子,全角狀態ア
字符開頭的爲アいろはアイウエイウエ
,半角狀態ア
字符開頭的爲アいろはアイウエイウエ
。能夠看到,明顯同一個片假字的全角狀態 比半角狀態 「胖」一圈。
英文字母其實也有全角字母,例如小寫的a
,其半角形式的unicode碼爲0061,其全角形式的unicode碼爲FF41。可查閱Unicode®字符百科官網。
NSStringCompareOptions是一個枚舉類型,並不是一個類。打開NSStringCompareOptions的定義,可查看以下
typedef NS_OPTIONS(NSUInteger, NSStringCompareOptions) {
NSCaseInsensitiveSearch = 1,
NSLiteralSearch = 2, /* Exact character-by-character equivalence */
NSBackwardsSearch = 4, /* Search from end of source string */
NSAnchoredSearch = 8, /* Search is limited to start (or end, if NSBackwardsSearch) of source string */
NSNumericSearch = 64, /* Added in 10.2; Numbers within strings are compared using numeric value, that is, Foo2.txt < Foo7.txt < Foo25.txt; only applies to compare methods, not find */
NSDiacriticInsensitiveSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 128, /* If specified, ignores diacritics (o-umlaut == o) */
NSWidthInsensitiveSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 256, /* If specified, ignores width differences ('a' == UFF41) */
NSForcedOrderingSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 512, /* If specified, comparisons are forced to return either NSOrderedAscending or NSOrderedDescending if the strings are equivalent but not strictly equal, for stability when sorting (e.g. "aaa" > "AAA" with NSCaseInsensitiveSearch specified) */
NSRegularExpressionSearch API_AVAILABLE(macos(10.7), ios(3.2), watchos(2.0), tvos(9.0)) = 1024 /* Applies to rangeOfString:..., stringByReplacingOccurrencesOfString:..., and replaceOccurrencesOfString:... methods only; the search string is treated as an ICU-compatible regular expression; if set, no other options can apply except NSCaseInsensitiveSearch and NSAnchoredSearch */
};
複製代碼
官方解釋:Added in 10.2; Numbers within strings are compared using numeric value, that is, Foo2.txt < Foo7.txt < Foo25.txt; only applies to compare methods, not find
void handleSortingForStrArray(void){
NSArray *stringsArray = [NSArray arrayWithObjects:
@"string b",
@"string A",
@"string a",
@"string \uFF41",
@"string a",
@"string A",
@"string c",
@"string d0030",
@"string d2",
@"アいろはアイウエイウエ",
@"アいろはアイウエイウエ",
@"アいろはアイウエイウエ",nil];
NSStringCompareOptions comparisonOptions = NSNumericSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
NSComparator finderSortBlock = ^(id string1,id string2) {
NSRange string1Range =NSMakeRange(0, [string1 length]);
return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
};
NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];
NSLog(@"finderSortArray: %@", finderSortArray);
}
複製代碼
NSStringCompareOptions
指定爲NSNumericSearch
,當字符串中含有數字時,從數值大小的角度按升序排序。官方解釋:無。英文字面解釋:不區分字母大小寫。
NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch;
複製代碼
NSStringCompareOptions
指定爲NSCaseInsensitiveSearch
,不區分同一個字母的大小寫狀態,如a
與A
看作相同元素,若其它條件也一致則保持原序。官方解釋:Exact character-by-character equivalence
NSStringCompareOptions comparisonOptions = NSLiteralSearch;
複製代碼
結論
題外話
官方解釋:If specified, ignores width differences ('a' == UFF41)
NSStringCompareOptions comparisonOptions = NSWidthInsensitiveSearch;
複製代碼
UFF41
是指全角a
,'a'
是指半角a
,若是指定NSWidthInsensitiveSearch,則不區分字符的全角半角,即便你同時指定了NSLiteralSearch。即,當有以下代碼
NSStringCompareOptions comparisonOptions = NSWidthInsensitiveSearch | NSLiteralSearch;
複製代碼
其做用至關於沒有NSLiteralSearch的代碼
NSStringCompareOptions comparisonOptions = NSWidthInsensitiveSearch;
複製代碼
官方解釋:If specified, comparisons are forced to return either NSOrderedAscending or NSOrderedDescending if the strings are equivalent but not strictly equal, for stability when sorting (e.g. "aaa" > "AAA" with NSCaseInsensitiveSearch specified)
NSStringCompareOptions comparisonOptions = NSForcedOrderingSearch;
複製代碼
NSStringCompareOptions comparisonOptions = NSNumericSearch|NSWidthInsensitiveSearch|NSForcedOrderingSearch;
複製代碼
NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch|NSNumericSearch|NSWidthInsensitiveSearch|NSForcedOrderingSearch;
複製代碼
這裏面,NSCaseInsensitiveSearch是爲了避免區分大小寫字母,可是後面再加個NSForcedOrderingSearch想強制區分字符又是怎麼回事?雖然,這樣寫並不會報錯,運行效果跟上面的綜合示例一摸同樣。但這樣誤導的想法是個邏輯矛盾。不信,你看看它運行的結果:
需求:假設咱們根據後臺返回的JSON字典數組用MJExtension轉換成模型數組,如今咱們須要根據ID或者Age對模型數組進行排序。
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic,copy) NSString *ID;
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@end
複製代碼
NSArray *sortArrayByAgeInt = [self.dataArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
Person *pModel1 = obj1;
Person *pModel2 = obj2;
if (pModel1.age > pModel2.age) {
return NSOrderedDescending;//降序
}else if (pModel1.name < pModel2.name){
return NSOrderedAscending;//升序
}else {
return NSOrderedSame;//相等
}
}];
複製代碼
NSArray *sortArrayByIDStr = [self.dataArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
Person *pModel1 = obj1;
Person *pModel2 = obj2;
if ([pModel1.ID intValue]> [pModel2.ID intValue]) {
return NSOrderedDescending;//降序
}else if (pModel1.name < pModel2.name){
return NSOrderedAscending;//升序
}else {
return NSOrderedSame;//相等
}
}];
複製代碼
在OC的高級用法中,常常須要查看系統類或者某個自定義類中的私有屬性以及私有成員變量,並經過KVC的辦法強制修改這些私有成員變量的值,以取代系統或者自定義類中的默認設置。因此,若是你懶得建立一些假數據的數組,能夠想到運用運行時的辦法獲取成員變量的數組,並進行排序操做訓練。
題1. 請取出
NSString
類的所有公有 屬性 並存放到一個數組,並利用NSArray
的sortedArrayUsingComparator
的方法給這個數組進行升序排序操做。要求:排序過程當中須要區分字符全角半角狀態,其它可按系統默認條件。
void handlePrintingOfProperties(void){
unsigned int count;// 記錄屬性個數
objc_property_t *properties = class_copyPropertyList([NSString class], &count);
// 生成一個屬性名稱組成的數組
NSMutableArray *propertyNameArray = [NSMutableArray array];
for (int i = 0; i < count; i++) {
// An opaque type that represents an Objective-C declared property.
// objc_property_t 屬性類型
objc_property_t property = properties[i];
// 獲取屬性的名稱 C語言字符串
const char *cName = property_getName(property);
// 轉換爲Objective C 字符串
NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
[propertyNameArray addObject:name];
}
NSLog(@"排序前的屬性列表 = %@",propertyNameArray);
NSComparator cmptr = ^(NSString *obj1, NSString *obj2){
return [obj1 compare:obj2 options:NSLiteralSearch];
};
NSArray *afterSort = [propertyNameArray sortedArrayUsingComparator:cmptr];
NSLog(@"排序後的屬性列表 = %@",afterSort);
//C語言中,用完copy,create的東西以後,最好釋放
free(properties);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"handlePrintingOfProperties()**********************");
handlePrintingOfProperties();
}
return 0;
}
複製代碼
題2. 請取出
NSURL
類中包括私有 在內的所有 成員變量,並存放到一個數組,並利用NSArray
的sortedArrayUsingComparator
的方法給這個數組進行升序排序操做。要求:排序過程當中須要區分字符全角半角狀態,其它可按系統默認條件。
void handlePrintingOfIvars(void){
unsigned int count;// 記錄屬性個數
Ivar *properties = class_copyIvarList([NSURL class], &count);
// 生成一個屬性名稱組成的數組
NSMutableArray *propertyNameArray = [NSMutableArray array];
for (int i = 0; i < count; i++) {
// An opaque type that represents an Objective-C declared property.
// objc_property_t 屬性類型
Ivar property = properties[i];
// 獲取屬性的名稱 C語言字符串
const char *cName = ivar_getName(property);
// 轉換爲Objective C 字符串
NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
[propertyNameArray addObject:name];
}
NSLog(@"排序前的成員變量列表 = %@",propertyNameArray);
NSComparator cmptr = ^(NSString *obj1, NSString *obj2){
return [obj1 compare:obj2 options:NSLiteralSearch];
};
NSArray *afterSort = [propertyNameArray sortedArrayUsingComparator:cmptr];
NSLog(@"排序後的成員變量列表 = %@",afterSort);
//C語言中,用完copy,create的東西以後,最好釋放
free(properties);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"handlePrintingOfIvars()**********************");
handlePrintingOfIvars();
}
return 0;
}
複製代碼
任何能在計算機上執行的項目稱之爲程序,其中,有圖形化用戶界面的程序稱之爲應用 ,沒有圖形界面的程序能夠是守護進程 ,還有一種稱之爲命令行工具。本文這裏關注的是算法和數據結果,不關注圖形界面,因此新建一個命令行工具便可。建立方法:新建一個macOS工程,選擇Command Line Tool類型,點擊下一步配置工程信息便可。