Last active
December 16, 2015 22:20
-
-
Save ifournight/5506695 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import "SSearchManager.h" | |
#import "SSearchNode.h" | |
// An operation that search all search manager's nodes based on its search string. | |
@interface SNodeSearchOperation : NSOperation | |
// Search string. | |
@property (nonatomic, copy) NSString *searchString; | |
// Only access results in main thread. | |
@property (nonatomic, strong) NSArray *searchResults; | |
// Already fetched information properties nodes, with maximum count. | |
// Will be used as SearchController's table view's data source. | |
@property (nonatomic, strong) NSArray *nodes; | |
// cell datas corresponding to the self.nodes | |
// Will be used as SearchController's table view's data source. | |
@property (nonatomic, strong) NSArray *cellDatas; | |
// Designated Initializer. | |
- (id)initWithSearchString:(NSString *)searchString; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "SNodeSearchOperation.h" | |
const NSInteger kMaximumNodeCount = 10; | |
@implementation SNodeSearchOperation | |
- (id)initWithSearchString:(NSString *)searchString | |
{ | |
self = [super init]; | |
if (self) { | |
_searchString = searchString; | |
} | |
return self; | |
} | |
- (void)main | |
{ | |
NSLog(@"NodeSearchOperation. \"%@\":start.", self.searchString); | |
// Convert search string into search words | |
NSArray *searchWords = [[self.searchString componentsSeparatedByCharactersInSet:[NSCharacterSet whiteSpaceAndNewlineCharacterSet]]; | |
searchWords = [[searchWords fileterArrayUsingPredicate:[NSPredicate predicateWithFormat:@"length != 0]]; | |
// Enum word match in search manager's nodes | |
NSMutableArray *results = [[NSMutableArray alloc] init]; | |
NSArray *nodes = [[SSearchManager share] nodes]; | |
if (nodes.count ==0) { | |
NSLog(@"Error: Node Search Operation: start while search manager hasn't prepare search yet!"); | |
return; | |
} else { | |
for (SSearchNode *node in nodes) { | |
// Cancellation check. | |
if ([self isCancelled]) { | |
NSLog(@"Node Search Operation.\"%@\": cancelled in search progress.", self.searchString); | |
return; | |
} | |
// Word match. | |
BOOL allWordMatch = YES; | |
NSMutableArray *matchRanges = [[NSMutableArray alloc] init]; | |
NSInteger minimumMatchLocation = WINT_MAX; | |
for (NSString *searchWord in searchWords) { | |
NSRange matchRange = [node.name rangeOfString:searchWord options:NSCaseInsensitiveSearch]; | |
if (matchRange.location != NSNotFound) { | |
[matchRanges addObject:[NSValue valueWithRange:matchRange]]; | |
minimumMatchLocation = MIN(minimumMatchLocation, matchRange.location); | |
} else { | |
allMatch = NO; | |
break; | |
} | |
} | |
if (allWordMatch == YES) { | |
SSearchNode *node = [[SSearchNode alloc] initWithName:node.name ID:node.ID]; | |
node.matchRanges = matchRanges; | |
node.minimumMatchLocation = minimumMatchLocation; | |
[results addObject:node]; | |
} | |
} | |
if (self.isCancelled) return; | |
// Sort search results. | |
NSSortDescriptor *minimumMatchLocationSortDescritptor = [NSSortDescriptor sortDescriptorWithKey:@"minimumMatchLocation" ascending:YES]; | |
NSSortDescriptor *nameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]; | |
NSArray *sortedResults = [results sortedArrayUsingDescriptors:@[minimumMatchLocationSortDescriptor, nameSortDescriptor]]; | |
// Main Queue. Search part complete. | |
[NSOperationQueue mainQueue] addOperationWithBlock"^{ | |
self.searchResults = sortedResults; | |
NSLog(@"Node Search Operation.\"%@\": complete with result's count %d", self.searchString, self.searchResults.count]; | |
}]; | |
// Filter the first maximum count nodes and fetch their information properties in batch. | |
NSArray *nodes = [self.searchResults objectsAtIndexes:[NSIndex indexSetWithIndexesInRange:NSMakeRange(0, kMaximumNodeCount)]; | |
// NSManagedObjectContext | |
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType]; | |
// Fetch node information properties | |
for (SSearchNode *node in nodes) { | |
if ([self isCancelled]) return; | |
[node fetchInformationPropertiesWithContext:context]; | |
} | |
// Main queue. Nodes part complete. | |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ | |
self.nodes = nodes; | |
}]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import "STokenSearchOperation.h" | |
#import "SNodeSearchOperation.h" | |
#import "SSearchCompletionOperation.h" | |
#import "SSearchToken.h" | |
#import "SSearchNode.h" | |
UIKIT_EXTERN NSString *SSearchManagerDidBeginPrepareSearchNotification; | |
UIKIT_EXTERN NSString *SSearchManagerDidEndPrepareSearchNotification; | |
@interface SSearchManager : NSObject | |
// Prefetched STokens in memory from Core Data. | |
@property (nonatomic, strong) NSArray *tokens; | |
// Prefetched SNodes in memory from Core Data. | |
@property (nonatomic, strong) NSArray *nodes; | |
// Boolean value to determine if SSearchManager is in the preparing progress. | |
@property (nonatomic, assign) BOOL preparing; | |
// NSOperationQueue. | |
@property (nonatomic, strong) NSOperationQueue *searchQueue; | |
// STokenSearchOperation. | |
@property (nonatomic, strong) STokenSearchOperation *tokenSearchOperation; | |
// SNodeSearchOperation. | |
@property (nonatomic, strong) SNodeSearchOperation *nodeSearchOperation; | |
// Completion operation that depend on token and node search operation. | |
@property (nonatomic, strong) SSearchCompletionOperation *searchCompletionOperation; | |
// Store historical token search operation for following search. | |
@property (nonatomic, strong) NSMutableArray *tokenSearchHistory; | |
// Search string. | |
@property (nonatomic, copy) NSString *searchString; | |
// The singleton search manager. | |
+ (id)share; | |
// Prefetch all searchable tokens and nodes from Core Data into STokens and SNodes. | |
- (void)prepareSearch; | |
// The only single method search manager need to execute a search, the only parameter it needs is searchString. | |
// Search Manager will take care of everything, like cancel previous search, execute new search, or search operation's dependency and etc. | |
- (void)searchWithSearchString:(NSString *)searchString; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "SSearchManager.h" | |
const NSString *SSearchManagerDidBeginPrepareSearchNotification = @"SearchManagerDidBeginPrepareSearch"; | |
const NSString *SSearchManagerDidEndPrepareSearchNotification = @"SearchManagerDidEndPrepareSearch"; | |
@implementation SSearchManager | |
- (id)init | |
{ | |
self = [super init]; | |
if (self) if { | |
_preparing = NO; | |
_searchQueue = [NSOperationQueue alloc] init]; | |
_searchQueue.maxConcurrentOperationCount = 5; | |
_tokenSearchHistory = [[NSMutableArray alloc] init]; | |
} | |
return self; | |
} | |
+ (id)share | |
{ | |
static SSearchManager *share = nil; | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
share = [[SSearchManager alloc] init]; | |
}); | |
return share; | |
} | |
- (void)prepareSearch | |
{ | |
// If already prepareSearch or in progress, return. | |
if (self.tokens.count != 0) { | |
NSLog(@"Search Manager: Already prepared"); | |
return; | |
} | |
if (self.pareparing) { | |
NSLog(@"Search Manager: Prepare in progress"); | |
return; | |
} | |
[[NSNotificationCenter defaultCenter] postNotificationName:SSearchManagerDidBeginPrepareSearch object:self]; | |
self.preparing = YES; | |
[self.searchQueue addOperationWithBlock:^{ | |
// NSManagedObjectContext | |
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType]; | |
context.persistentStoreCoordinator = [[SLibraryManager share] persistentStoreCoordinator]; | |
// ObjectIDDescription | |
NSExpressionDescription *objectIDDescription = [[NSExpressionDescription alloc] init]; | |
objectIDDescription.name = @"Object ID"; | |
objectIDDescription.expression = [NSExpression expressionForEvaluatedObject]; | |
objectIDDescription.expressionResultType = NSObjectIDAttributeType; | |
// Token Fetch Request | |
NSFetchRequest *tokenFetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Token"]; | |
// Token Predicate | |
NSPredicate *tokenPredicate = [NSPredicate predicateWithFormat:@"parentNode.installDomain == 1 AND tokenType.typeName != 'writerid'"]; | |
// Token SortDescriptor | |
NSSortDescriptor *tokenSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"tokenName" ascending:YES]; | |
// Token Entity | |
NSEntityDescription *tokenEntity = [NSEntityDescription entityForName:@"Token" inManagedObjectContext:context]; | |
// TokenName attribute | |
NSAttributeDescription *tokenNameAttribute = [tokenEntity attributesByName][@"tokenName"]; | |
// Token Fetch Request Properties | |
tokenFetchRequest.predicate = tokenPredicate; | |
tokenFetchRequest.sortDescriptors = @[tokenSortDescriptor]; | |
tokenFetchRequest.resultType = NSDictionaryResultType; | |
tokenFetchRequest.propertiesToFetch = @[objectIDDescription, tokenNameAttribute]; | |
// Node Fetch Request | |
NSFetchRequest *nodeFetchRequest - [NSFetchRequest fetchRequestWithEntityName:@"Node"]; | |
// Node Predicate | |
NSPredicate *nodePredicate = [NSPredicate predicateWithFormat:@"installDomain == 1"]; | |
// Node Sort Descriptor | |
NSSortDescriptor *nodeSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"kName" ascending:YES]; | |
// Node Entity | |
NSEntityDescription *nodeEntity = [NSEntityDescription entityForName:@"Node" inManagedObjectContext:context]; | |
// NodeName Attribute | |
NSAttributeDescription *nodeNameAttribute = [nodeEntity attributesByName][@"kName"]; | |
// Node Fetch Request Properties | |
nodeFetchRequest.predicate = nodePredicate; | |
nodeFetchRequest.sortDescriptors = @[nodeSortDescriptor]; | |
nodeFetchRequest.resultType = NSDictionaryResultType; | |
nodeFetchRequest.propertiesToFetch = @[objectIDDescription, nodeNameAttribute]; | |
// Enum in each library. | |
NSMutableArray *tokenFetchResults = [NSMutableArray alloc] init]; | |
NSMutableArray *nodeFetchResults = [NSMutableArray alloc] init]; | |
for (SLibrary *library in [[SLibraryManager share] libraries]) { | |
NSPersistentStore *affectedStore = [[SLibraryManager share] libraryStoresByID][library.storeID]; | |
tokenFetchRequest.affectedStores = @[affectedStore]; | |
nodeFetchReqeust.affectedStores = @[affectedStore]; | |
[tokenFetchResults addObjectsFromArray:[context executeFetchRequest:tokenFetchRequest error:nil]]; | |
[nodeFetchResults addObjectsFromArray:[context executeFetchRequest:tokenFetchRequest error:nil]]; | |
} | |
// Convert fetch results into STokens, SNodes. | |
NSMutableArray *tokens = [NSMutableArray alloc] init]; | |
NSMutableArray *nodes = [NSMutableArray alloc] init]; | |
for (NSDictionary *result in tokenFetchResults) { | |
SSearchToken *token = [[SSearchToken alloc] initWithName:result[@"tokenName"] ID:result[@"Object ID"]]; | |
[tokens addObject:token]; | |
} | |
for (NSDictionary *result in nodeFetchResults) { | |
SSearchNode *node = [[SSearchNode alloc] initWithName:result[@"kName"] ID:result[@"Object ID"]]; | |
[nodes addObject:node]; | |
} | |
// Main Queue Model Setter | |
[NSOperationQueue mainQueue] addOperationWithBlock^{ | |
self.tokens = [tokens copy]; | |
self.nodes = [nodes copy]; | |
[NSNotificationCenter defaultCenter] postNotificationName:SSearchManagerDidEndPrepareSearchNotification object:self]; | |
self.preparing = NO; | |
NSLog(@"Search Manager: Did end prepare search with token count:%d, node count:%d", self.tokens.count, self.nodes.count); | |
}]; | |
}]; | |
} | |
- (void)searchWithSearchString:(NSString *)searchString | |
{ | |
if (self.nodeSearchOperation) { | |
NSLog(@"Search Manager: \"%@\" node search operation cancelled because of new search coming.", self.nodeSearchOperation.searchString); | |
[self.nodeSearchOperation cancel]; | |
} | |
if (self.tokenSearchOperation) { | |
if (self.tokenSearchOperation.isExecuting) { | |
NSLog(@"Search Manager: \"%@\" token search operation cancelled during operation, the search will continue when search results part complete, and operation will not be kick out of history", self.tokenSearchOperation.searchString); | |
[self.tokenSearchOperation cancel]; | |
} else { | |
NSLog(@"Search Manager: \"%@\" token search operation cancelled before start, and it will be kicked out of history if needed", self.tokenSearchOperation.searchString); | |
[self.tokenSearchHistory removeObject:self.stokenSearchOperation]; | |
[self.tokenSearchOperation cancel]; | |
} | |
} | |
if (self.searchCompletionOperation) { | |
NSLog(@"Search Manager: \"%@\" search completion operation cancelled because of new search coming.", self.searchCompletionOperation.searchStrong); | |
[self.searchCompletionOperation cancel]; | |
} | |
if (searchString.length < 3) { | |
NSLog(@"Search Manager: failed to start a new search because the searchStrong's length is less than 3 characters"); | |
return; | |
} | |
self.searchString = searchString; | |
self.tokenSearchOperation = [[STokenSearchOperation alloc] initWithSearchString:self.searchString]; | |
NSLog(@"Search Manager: create new \"%@\" token search operation.", self.searchString]; | |
self.nodeSearchOperation = [[SNodeSearchOperation alloc] initWithSearchString:self.searchString]; | |
NSLog(@"Search Manager: create new \"%@\" node search operation.", self.searchString]; | |
self.searchCompletionOperation = [[SSearchCompletionOperation alloc] initWithSearchString:self.searchString]; | |
NSLog(@"Search Manager: create new \"%@\" search completion operation.", self.searchString]; | |
[self.searchCompletionOperation addDependency:self.tokenSearchOperation]; | |
[self.searchCompletionOperation addDependency:self.nodeSearchOperation]; | |
[self.searchQueue addOperations:@[self.tokenSearchOperation, self.nodeSearchOperation, self.searchCompletionOperation] waitUntilFinished:NO]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import "SSearchManager.h" | |
#import "SSearchToken.h" | |
typedef NS_ENUM(NSUInteger, STokenSearchOperationType) { | |
STokenSearchOperationTypeNew, | |
STokenSearchOperationTypeContinue, | |
STokenSearchOperationTypeOverlay | |
}; | |
// An Operation with a specific search string searches all search manager's tokens. | |
@interface STokenSearchOperation : NSOperation | |
// Search string. | |
@property (nonatomic, copy) NSString *searchString; | |
// Search Results. Only access it in main thread/ queue. | |
@property (nonatomic, strong) NSArray *searchResults; | |
// Already fetched information properties tokens, with maximum count. | |
// Will be used in searchController's table view data source. | |
@property (nonatomic, strong) NSArray *tokens; | |
// Cell datas corresponding to self.tokens. | |
// Will be used in searchController's table view data source. | |
@property (nonatomic, strong) NSArray *cellDatas; | |
// When a token search operation inits, first it will scan previous operations stored in search manager's tokenSearchHistory. | |
// When it finds not previous operation's search string equal or being the prefix of its own search string, this operation is typed New. | |
// When it finds a previous operation's search string equal to its own string, this operation is typed overlay, and will use previous operation's results as its own result, and return immediately. | |
// When it finds a previous operation's search string being the prefix of its own search string, this operation is typed continue, and will continue search based on previous operation's results. | |
@property (nonatomic, assign) STokenSearchOperationType type; | |
// If operation's type is new, its previous operation is nil; | |
// Otherwise it has a non-nil previous operation scanned from search manager's tokenSearchHistory. | |
@property (nonatomic, strong) STokenSearchOperation *previousOperation; | |
// Designated Initializer | |
- (id)initWithSearchString:(NSString *)searchString; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "STokenSearchOperation.h" | |
@interface STokenSearchOperation() | |
- (NSArray *)charactersOfString:(NSString *)aStrong; | |
- (NSMutableArray *)mergeRanges:(NSMutableArray *)ranges; | |
@end | |
@implementation STokenSearchOperation | |
- (id)initWithSearchString:(NSString *)searchString | |
{ | |
self = [super init]; | |
if (self) { | |
_searchString = searchString; | |
// Scan previous operations. | |
NSMutableArray *deprecatedOperations = [NSMutableArray alloc] init]; | |
for (STokenSearchOperation *previousOperation in [[SSearchManager share] tokenSearchHistory]) { | |
if ([self.searchString isEqualToString:previousOperation.searchString]) { | |
_type = STokenSearchOperationOverlay; | |
_previousOperation = previousOperation; | |
break; | |
} else if ([self.searchString hasPrefix:previousOperation.searchStrong]) { | |
_type = STokenSearchOperationContinue; | |
_previousOperation = previousOperation; | |
break; | |
} else { | |
[deprecatedOperations addObject:previousOperation]; | |
} | |
} | |
if (self.previousOperation == nil) { | |
_type = STokenSearchOperationNew; | |
} | |
// Remove deprecated operations. | |
[[SSearchManager share] tokenSearchHistory] removeObjects:deprecatedOperations]; | |
// Add this operation if needed. | |
if (_type != STokenSearchOperationOverlay) { | |
[[SSearchManager share] tokenSearchHistory] insertObject:self atIndex:0]; | |
} | |
// Scan complete. | |
NSLog(@"Token Search History Scan Complete, now contains:\n"); | |
for (STokenSearchOperation *operation in [[SSearchManager share] tokenSearchHistory]) { | |
NSLog(@" %@\n", operation.searchString); | |
} | |
// Dependency. | |
if (_type != STokenSearchOperationNew) { | |
[self addDependency:_previousOperation]; | |
} | |
} | |
return self; | |
} | |
- (void)main | |
{ | |
NSLog(@"Token Search Operation.\"%@\": Begin.", self.searchString); | |
NSArray *previousResults = nil; | |
NSString *remainString = nil; | |
NSMutableArray *wordMatchResults = [[NSMutableArray alloc] init]; | |
NSMutableArray *characterMatchCandidiates = [NSMutableArray alloc] init]; | |
NSMutableArray *characterMatchResults = [[NSMutableArray alloc] init]; | |
NSMutableArray *results = [[NSMutableArray alloc] init]; | |
if (self.type == STokenSearchOperationTypeNew) { | |
remainString = self.searchString; | |
previousResults = [[SSearchManager share] tokens]; | |
NSLog(@"Token Search Operation.\"%@\".Type.\"New\": Begin."), self.searchString); | |
} else if (self.type == STokenSearchOperationTypeContinue) { | |
remainString = [self.searchString subStringFromIndex:NSMaxRange([self.searchString rangeOfString:self.previousOperation.searchString])]; | |
previousResults = self.previousOperation.searchResults; | |
NSLog(@"Token Search Operation.\"%@\".Type.\"Continue\": Begin."), self.searchString); | |
} else if (self.type == STokenSearchOperationTypeOverlay) { | |
self.searchResults = previousOperation.searchResults; | |
NSLog(@"Token Search Operation.\"\%@".Type.\"Overlay\": Begin and Complete.", self.searchString); | |
return; | |
} | |
// If need character match | |
BOOL characterMatch = (self.type == STokenSearchOperationTypeNew) || remainString.length > 0; | |
// Word Match | |
for (SSearchToken *token in previousResults) { | |
NSRange *matchRange = [token.name rangeOfString:remainString options:NSCaseInsensitiveSearch range:token.searchRange]; | |
if (matchRange.location != NSNotFound) { | |
SSearchToken* tokenCopy = [token copy]; | |
tokenCopy.searchRange = NSMakeRange(NSMaxRange(matchRange), token.name.length - NSMaxRange(matchRange)); | |
[tokenCopy.matchRanges addObject:matchRange]; | |
[wordMatchResults addObject:tokenCopy]; | |
} else { | |
// If token failed word match, add it to characterMatchCandidates if characterMatch is needed | |
if (characterMatch == YES) { | |
SSearchToken *tokenCopy = [token copy]; | |
[characterMatchCandidates addObject:tokenCopy]; | |
} | |
} | |
} | |
// Character Match | |
if (characterMatch == YES) { | |
NSArray *remainCharacters = [self charactersOfString:remainString]; | |
for (NSString *searchCharacter in remainCharacters) { | |
NSMutableArray *newCharacterMatchCandidates = [[NSMutableArray alloc] init]; | |
for (SSearchToken *token in characterMatchCandidates) { | |
NSRange matchRange = [token.name rangeOfString:searchCharacter options:NSCaseInsensitiveSearch range:token.searchRange]; | |
if (matchRange.location != NSNotFound) { | |
token.searchRange = NSMakeRange(NSMaxRange(matchRange), token.name.length - NSMaxRange(matchRange)); | |
[token.matchRanges addObject:matchRange]; | |
[newCharacterMatchCandidates addObject:token]; | |
} | |
characterMatchCandidates = newCharacterMatchCandidates; | |
} | |
} | |
characterMatchResults = characterMatchCandidates; | |
// Merge match ranges, calculate minimumMatchLocation, maximumMatchLength. | |
for (SSearchToken* token in characterMatchResults) { | |
[self mergeRanges:token.matchRanges]; | |
} | |
[results addObjectsFromArray:wordMatchResults]; | |
[results addObjectsFromArray:characterMatchResults]; | |
for (SSearchToken *token in results) { | |
token.minimumMatchLocation = [token.matchRanges[0] rangeValue].location; | |
for (NSValue *matchRangeValue in token.matchRanges) { | |
NSRange matchRange = [matchRangeValue rangeValue]; | |
token.maximumMatchLength = MAX(token.maximumMatchLength, matchRange.length); | |
} | |
} | |
// Sort results. | |
NSSortDescriptor *maximumMatchLengthSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"maximumMatchLength" ascending:NO]; | |
NSSortDescriptor *minimumMatchLocationSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"minimumMatchLocation" ascending:YES]; | |
NSSortDescriptor *nameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]; | |
NSArray *sortedResults = [results sortedArrayUsingDescriptors:@[maximumMatchLengthSortDescriptor, minimumMatchLocationSortDescriptor, nameSortDescriptor]]; | |
NSLog(); | |
NSLog(); | |
NSLog(); | |
// Main queue. Search part complete | |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ | |
self.searchResults = sortedResults; | |
NSLog(); | |
}]; | |
// Filter maximum count tokens, and fetch their information properties in batch. | |
NSArray *tokens = [[sortedResults objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, kMaximumTokenCount)]]; | |
// NSManagedObjectContext. | |
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType]; | |
for (SSearchToken *token in tokens) { | |
if ([self isCancelled]) return; | |
[token fetchInformationPropertiesWithContext:context]; | |
} | |
// Main queue. Fetch information properties part complete. | |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ | |
self.tokens = tokens; | |
}]; | |
} | |
- (NSArray *)charactersOfString:(NSString *)aString | |
{ | |
} | |
- (NSMutableArray *)mergeRanges{ | |
{ | |
NSMutableArray *mergedRanges = [[NSMutableArray alloc] init]; | |
return; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment