首先看下demo效果,下載地址git
有且只有
如下三大類總計 25 種匹配
漢語全拼拼音及每一個拼音字母所對應漢字的位置
和 漢語簡拼拼音和每一個拼音字母對應漢字的位置
,將初始化以後的信息緩存起來+ (instancetype)personWithName:(NSString *)name hanyuPinyinOutputFormat:(HanyuPinyinOutputFormat *)pinyinFormat {
WPFPerson *person = [[WPFPerson alloc] init];
/** 將漢字轉化爲拼音的類方法 * name : 須要轉換的漢字 * pinyinFormat : 拼音的格式化器 * @"" : seperator 分隔符 */
NSString *completeSpelling = [PinyinHelper toHanyuPinyinStringWithNSString:name withHanyuPinyinOutputFormat:pinyinFormat withNSString:@""];
// 首字母所組成的字符串
NSString *initialString = @"";
// 全拼拼音數組
NSMutableArray *completeSpellingArray = [[NSMutableArray alloc] init];
// 拼音首字母的位置數組
NSMutableArray *pinyinFirstLetterLocationArray = [[NSMutableArray alloc] init];
// 遍歷每個字符
for (NSInteger x =0; x<name.length; x++) {
NSRange range = NSMakeRange(x, 1);
// 獲取字符
NSString* hanyuCharString = [name substringWithRange:range];
// 若是該字符是中文
if ([WPFPinYinTools isChinese:hanyuCharString]) {
// 獲取該字符的第一個拼音字母,如 wang 的 firstLetter 就是 w
NSString *firstLetter = [WPFPinYinTools firstCharactor:hanyuCharString withFormat:pinyinFormat];
// 獲取該字符的拼音全拼,如 王 的 pinyinString就是 wang
NSString *pinyinString = [PinyinHelper toHanyuPinyinStringWithNSString:hanyuCharString withHanyuPinyinOutputFormat:pinyinFormat withNSString:@""];
/** 👉 👉獲取該字符的拼音在整個字符串中的位置,如 "wang peng fei" 👈 👈 * 👉 👉 "wang" 對應的四個拼音字母是 0,0,0,0, 👈 👈 * 👉 👉 "peng" 對應的四個拼音字母是 1,1,1,1, 👈 👈 * 👉 👉 "fei" 對應的三個拼音字母是 2,2,2, 👈 👈 */
for (NSInteger j= 0 ;j<pinyinString.length ; j++) {
[completeSpellingArray addObject:@(x)];
}
// 拼接首字母字符串,如 "王鵬飛" 對應的首字母字符串就是 "wpf"
initialString = [initialString stringByAppendingString:firstLetter];
// 👉 👉 拼接首字母位置字符串,如 "王鵬飛" 對應的首字母位置就是 "0,1,2" 👈 👈
[pinyinFirstLetterLocationArray addObject:@(x)];
} else {
[completeSpellingArray addObject:@(x)];
[pinyinFirstLetterLocationArray addObject:@(x)];
initialString = [initialString stringByAppendingString:hanyuCharString];
}
}
person.name = name;
person.completeSpelling = completeSpelling;
person.initialString = initialString;
person.pinyinLocationString = [completeSpellingArray componentsJoinedByString:@","];
person.initialLocationString = [pinyinFirstLetterLocationArray componentsJoinedByString:@","];
return person;
}
複製代碼
UISearchResultsUpdating
代理方法 - (void)updateSearchResultsForSearchController:(UISearchController *)searchController
來實時獲取輸入的最新關鍵字,並遍歷數據源,將匹配到的結果顯示出來// 更新搜索結果
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSLog(@"%@", searchController.searchBar.text);
[self.searchResultVC.resultDataSource removeAllObjects];
for (WPFPerson *person in self.dataSource) {
WPFSearchResultModel *resultModel = [WPFPinYinTools
searchEffectiveResultWithSearchString:searchController.searchBar.text.lowercaseString
nameString:person.name
completeSpelling:person.completeSpelling
initialString:person.initialString
pinyinLocationString:person.pinyinLocationString
initialLocationString:person.initialLocationString];
if (resultModel.highlightRang.length) {
person.highlightLoaction = resultModel.highlightRang.location;
person.textRange = resultModel.highlightRang;
person.matchType = resultModel.matchType;
[self.searchResultVC.resultDataSource addObject:person];
}
};
// 將匹配結果按照產品規則進行排序
[self.searchResultVC.resultDataSource sortUsingDescriptors:[WPFPinYinTools sortingRules]];
// 刷新tableView
dispatch_async(dispatch_get_main_queue(), ^{
[self.searchResultVC.tableView reloadData];
});
}
複製代碼
+ (WPFSearchResultModel *)searchEffectiveResultWithSearchString:(NSString *)searchStrLower
nameString:(NSString *)nameStrLower
completeSpelling:(NSString *)completeSpelling
initialString:(NSString *)initialString
pinyinLocationString:(NSString *)pinyinLocationString
initialLocationString:(NSString *)initialLocationString {
WPFSearchResultModel *searchModel = [[WPFSearchResultModel alloc] init];
NSArray *completeSpellingArray = [pinyinLocationString componentsSeparatedByString:@","];
NSArray *pinyinFirstLetterLocationArray = [initialLocationString componentsSeparatedByString:@","];
// 徹底中文匹配範圍
NSRange chineseRange = [nameStrLower rangeOfString:searchStrLower];
// 拼音全拼匹配範圍
NSRange complateRange = [completeSpelling rangeOfString:searchStrLower];
// 拼音首字母匹配範圍
NSRange initialRange = [initialString rangeOfString:searchStrLower];
// 漢字直接匹配
if (chineseRange.length!=0) {
searchModel.highlightedRange = chineseRange;
searchModel.matchType = MatchTypeChinese;
return searchModel;
}
NSRange highlightedRange = NSMakeRange(0, 0);
// MARK: 拼音全拼匹配
if (complateRange.length != 0) {
if (complateRange.location == 0) {
// 拼音首字母匹配從0開始,即搜索的關鍵字與該數據源第一個漢字匹配到,因此高亮範圍從0開始
highlightedRange = NSMakeRange(0, [completeSpellingArray[complateRange.length-1] integerValue] +1);
} else {
/** 若是該拼音字符是一個漢字的首個字符,如搜索「g」, * 就要匹配出「gai」、「ge」等「g」開頭的拼音對應的字符, * 而不該該匹配到「wang」、「feng」等非」g「開頭的拼音對應的字符 */
NSInteger currentLocation = [completeSpellingArray[complateRange.location] integerValue];
NSInteger lastLocation = [completeSpellingArray[complateRange.location-1] integerValue];
if (currentLocation != lastLocation) {
// 高亮範圍從匹配到的第一個關鍵字開始
highlightedRange = NSMakeRange(currentLocation, [completeSpellingArray[complateRange.length+complateRange.location -1] integerValue] - currentLocation +1);
}
}
searchModel.highlightedRange = highlightedRange;
searchModel.matchType = MatchTypeComplate;
if (highlightedRange.length!=0) {
return searchModel;
}
}
// MARK: 拼音首字母匹配
if (initialRange.length!=0) {
NSInteger currentLocation = [pinyinFirstLetterLocationArray[initialRange.location] integerValue];
NSInteger highlightedLength;
if (initialRange.location ==0) {
highlightedLength = [pinyinFirstLetterLocationArray[initialRange.length-1] integerValue]-currentLocation +1;
// 拼音首字母匹配從0開始,即搜索的關鍵字與該數據源第一個漢字匹配到,因此高亮範圍從0開始
highlightedRange = NSMakeRange(0, highlightedLength);
} else {
highlightedLength = [pinyinFirstLetterLocationArray[initialRange.length+initialRange.location-1] integerValue]-currentLocation +1;
// 高亮範圍從匹配到的第一個關鍵字開始
highlightedRange = NSMakeRange(currentLocation, highlightedLength);
}
searchModel.highlightedRange = highlightedRange;
searchModel.matchType = MatchTypeInitial;
if (highlightedRange.length!=0) {
return searchModel;
}
}
searchModel.highlightedRange = NSMakeRange(0, 0);
searchModel.matchType = NSIntegerMax;
return searchModel;
}
複製代碼
首先篩選出一個比較全的第三方庫 PinYin4Objc用於漢語轉拼音,拼音的 unicode 庫比較全,一些新的漢字也都能轉成拼音github
可是因爲該庫很久沒有更新,獲取拼音文件部分代碼不適合組件化的直接開發,所以我直接合到源文件裏面了數組
漢語轉拼音的格式緩存
// 獲取格式化器
+ (HanyuPinyinOutputFormat *)getOutputFormat {
HanyuPinyinOutputFormat *pinyinFormat = [[HanyuPinyinOutputFormat alloc] init];
/** 設置大小寫 * CaseTypeLowercase : 小寫 * CaseTypeUppercase : 大寫 */
[pinyinFormat setCaseType:CaseTypeLowercase];
/** 聲調格式 :如 王鵬飛 * ToneTypeWithToneNumber : 用數字表示聲調 wang2 peng2 fei1 * ToneTypeWithoutTone : 無聲調錶示 wang peng fei * ToneTypeWithToneMark : 用字符表示聲調 wáng péng fēi */
[pinyinFormat setToneType:ToneTypeWithoutTone];
/** 設置特殊拼音ü的顯示格式: * VCharTypeWithUAndColon : 以U和一個冒號表示該拼音,例如:lu: * VCharTypeWithV : 以V表示該字符,例如:lv * VCharTypeWithUUnicode : 以ü表示 */
[pinyinFormat setVCharType:VCharTypeWithV];
return pinyinFormat;
}
複製代碼
+ (NSArray *)sortingRules {
// 按照 matchType 順序排列,即優先展現 中文,其次是全拼匹配,最後是拼音首字母匹配
NSSortDescriptor *desType = [NSSortDescriptor sortDescriptorWithKey:@"matchType" ascending:YES];
// 優先顯示 高亮位置索引靠前的搜索結果
NSSortDescriptor *desLocation = [NSSortDescriptor sortDescriptorWithKey:@"highlightLoaction" ascending:YES];
return @[desType,desLocation];
}
複製代碼
####四. 循環方法測試及優化選擇過程服務器
在優化遍歷方法的過程當中,測試了幾種遍歷方法,這裏以輸入關鍵字「wang」爲測試數據,測試真機機型爲iPhone SE 10.3
微信
/** 2017-12-06 12:02:51.943006 HighlightedSearch[4459:1871193] w 2017-12-06 12:02:51.943431 HighlightedSearch[4459:1871193] 開始匹配,開始時間:2017-12-06 04:02:51 +0000 2017-12-06 12:02:51.980588 HighlightedSearch[4459:1871193] 匹配結束,結束時間:2017-12-06 04:02:51 +0000,耗時:0.0372 2017-12-06 12:02:52.284488 HighlightedSearch[4459:1871193] wa 2017-12-06 12:02:52.284771 HighlightedSearch[4459:1871193] 開始匹配,開始時間:2017-12-06 04:02:52 +0000 2017-12-06 12:02:52.316536 HighlightedSearch[4459:1871193] 匹配結束,結束時間:2017-12-06 04:02:52 +0000,耗時:0.0318 2017-12-06 12:02:52.516826 HighlightedSearch[4459:1871193] wan 2017-12-06 12:02:52.517121 HighlightedSearch[4459:1871193] 開始匹配,開始時間:2017-12-06 04:02:52 +0000 2017-12-06 12:02:52.545542 HighlightedSearch[4459:1871193] 匹配結束,結束時間:2017-12-06 04:02:52 +0000,耗時:0.0285 2017-12-06 12:02:52.838220 HighlightedSearch[4459:1871193] wang 2017-12-06 12:02:52.838602 HighlightedSearch[4459:1871193] 開始匹配,開始時間:2017-12-06 04:02:52 +0000 2017-12-06 12:02:52.880200 HighlightedSearch[4459:1871193] 匹配結束,結束時間:2017-12-06 04:02:52 +0000,耗時:0.0417 */
for (NSInteger i = 0; i < self.dataSource.count; i++) {
複製代碼
/** 2017-12-06 11:56:55.565738 HighlightedSearch[4419:1869486] w 2017-12-06 11:56:55.566287 HighlightedSearch[4419:1869486] 開始匹配,開始時間:2017-12-06 03:56:55 +0000 2017-12-06 11:56:55.626184 HighlightedSearch[4419:1869486] 匹配結束,結束時間:2017-12-06 03:56:55 +0000,耗時:0.0601 2017-12-06 11:56:55.937535 HighlightedSearch[4419:1869486] wa 2017-12-06 11:56:55.937842 HighlightedSearch[4419:1869486] 開始匹配,開始時間:2017-12-06 03:56:55 +0000 2017-12-06 11:56:55.983074 HighlightedSearch[4419:1869486] 匹配結束,結束時間:2017-12-06 03:56:55 +0000,耗時:0.0452 2017-12-06 11:56:56.344808 HighlightedSearch[4419:1869486] wan 2017-12-06 11:56:56.347350 HighlightedSearch[4419:1869486] 開始匹配,開始時間:2017-12-06 03:56:56 +0000 2017-12-06 11:56:56.414215 HighlightedSearch[4419:1869486] 匹配結束,結束時間:2017-12-06 03:56:56 +0000,耗時:0.0690 2017-12-06 11:56:56.711174 HighlightedSearch[4419:1869486] wang 2017-12-06 11:56:56.712013 HighlightedSearch[4419:1869486] 開始匹配,開始時間:2017-12-06 03:56:56 +0000 2017-12-06 11:56:56.774761 HighlightedSearch[4419:1869486] 匹配結束,結束時間:2017-12-06 03:56:56 +0000,耗時:0.0632 */
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(self.dataSource.count, queue, ^(size_t index) {
複製代碼
/** 2017-12-06 11:58:12.716606 HighlightedSearch[4428:1869917] w 2017-12-06 11:58:12.717005 HighlightedSearch[4428:1869917] 開始匹配,開始時間:2017-12-06 03:58:12 +0000 2017-12-06 11:58:12.780168 HighlightedSearch[4428:1869917] 匹配結束,結束時間:2017-12-06 03:58:12 +0000,耗時:0.0633 2017-12-06 11:58:13.058590 HighlightedSearch[4428:1869917] wa 2017-12-06 11:58:13.058841 HighlightedSearch[4428:1869917] 開始匹配,開始時間:2017-12-06 03:58:13 +0000 2017-12-06 11:58:13.116964 HighlightedSearch[4428:1869917] 匹配結束,結束時間:2017-12-06 03:58:13 +0000,耗時:0.0581 2017-12-06 11:58:13.397052 HighlightedSearch[4428:1869917] wan 2017-12-06 11:58:13.397338 HighlightedSearch[4428:1869917] 開始匹配,開始時間:2017-12-06 03:58:13 +0000 2017-12-06 11:58:13.460298 HighlightedSearch[4428:1869917] 匹配結束,結束時間:2017-12-06 03:58:13 +0000,耗時:0.0630 2017-12-06 11:58:13.763888 HighlightedSearch[4428:1869917] wang 2017-12-06 11:58:13.764263 HighlightedSearch[4428:1869917] 開始匹配,開始時間:2017-12-06 03:58:13 +0000 2017-12-06 11:58:13.833888 HighlightedSearch[4428:1869917] 匹配結束,結束時間:2017-12-06 03:58:13 +0000,耗時:0.0697 */
dispatch_queue_t queue = dispatch_queue_create("wpf.updateSearchResults.test", DISPATCH_QUEUE_SERIAL);
[self.dataSource enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
複製代碼
/** 2017-12-06 12:00:38.217187 HighlightedSearch[4439:1870645] w 2017-12-06 12:00:38.217575 HighlightedSearch[4439:1870645] 開始匹配,開始時間:2017-12-06 04:00:38 +0000 2017-12-06 12:00:38.253997 HighlightedSearch[4439:1870645] 匹配結束,結束時間:2017-12-06 04:00:38 +0000,耗時:0.0364 2017-12-06 12:00:38.616430 HighlightedSearch[4439:1870645] wa 2017-12-06 12:00:38.616807 HighlightedSearch[4439:1870645] 開始匹配,開始時間:2017-12-06 04:00:38 +0000 2017-12-06 12:00:38.654969 HighlightedSearch[4439:1870645] 匹配結束,結束時間:2017-12-06 04:00:38 +0000,耗時:0.0383 2017-12-06 12:00:38.948700 HighlightedSearch[4439:1870645] wan 2017-12-06 12:00:38.949453 HighlightedSearch[4439:1870645] 開始匹配,開始時間:2017-12-06 04:00:38 +0000 2017-12-06 12:00:38.986892 HighlightedSearch[4439:1870645] 匹配結束,結束時間:2017-12-06 04:00:38 +0000,耗時:0.0378 2017-12-06 12:00:39.280979 HighlightedSearch[4439:1870645] wang 2017-12-06 12:00:39.281563 HighlightedSearch[4439:1870645] 開始匹配,開始時間:2017-12-06 04:00:39 +0000 2017-12-06 12:00:39.317743 HighlightedSearch[4439:1870645] 匹配結束,結束時間:2017-12-06 04:00:39 +0000,耗時:0.0365 */
for (WPFPerson *person in self.dataSource) {
複製代碼
最終選擇的是forin循環,由於通常狀況下
enumerateObjectsWithOptions
多線程是最快的,而且稍快於dispatch_apply
方法,可是由於這個方法須要操做數組,所以必須將操做數據的那行代碼加鎖或者在指定線程進行,進行這個操做後效率反而不如其餘單線程循環,考慮到搜索結果原本還要再次根據規則排序,就選擇了 forin 循環多線程
簡單測了一下擁有該功能的產品:併發
// WPFPinYinDataManager 依次添加數據源(標識符爲了防止重名現象)
+ (void)addInitializeString:(NSString *)string identifer:(NSString *)identifier
// 更新搜索結果
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
...
...
for (WPFPerson *person in [WPFPinYinDataManager getInitializedDataSource]) {
WPFSearchResultModel *resultModel = [WPFPinYinTools searchEffectiveResultWithSearchString:keyWord Person:person];
if (resultModel.highlightedRange.length) {
person.highlightLoaction = resultModel.highlightedRange.location;
person.textRange = resultModel.highlightedRange;
person.matchType = resultModel.matchType;
[resultDataSource addObject:person];
}
}
複製代碼
最後再附一下demo地址app