// // FLEXRuntimeController.m // FLEX // // Created by Tanner on 3/23/17. // #import "TBRuntimeController.h" #import "TBRuntime.h" #import "FLEXMethod.h" @interface TBRuntimeController () @property (nonatomic, readonly) NSCache *bundlePathsCache; @property (nonatomic, readonly) NSCache *bundleNamesCache; @property (nonatomic, readonly) NSCache *classNamesCache; @property (nonatomic, readonly) NSCache *methodsCache; @end @implementation TBRuntimeController #pragma mark Initialization static TBRuntimeController *controller = nil; + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ controller = [self new]; }); return controller; } - (id)init { self = [super init]; if (self) { _bundlePathsCache = [NSCache new]; _bundleNamesCache = [NSCache new]; _classNamesCache = [NSCache new]; _methodsCache = [NSCache new]; } return self; } #pragma mark Public + (NSArray *)dataForKeyPath:(TBKeyPath *)keyPath { if (keyPath.bundleKey) { if (keyPath.classKey) { if (keyPath.methodKey) { return [[self shared] methodsForKeyPath:keyPath]; } else { return [[self shared] classesForKeyPath:keyPath]; } } else { return [[self shared] bundleNamesForToken:keyPath.bundleKey]; } } else { return @[]; } } + (NSArray *> *)methodsForToken:(TBToken *)token instance:(NSNumber *)inst inClasses:(NSArray *)classes { return [[TBRuntime runtime] methodsForToken:token instance:inst inClasses:classes ]; } + (NSMutableArray *)classesForKeyPath:(TBKeyPath *)keyPath { return [[self shared] classesForKeyPath:keyPath]; } + (NSString *)shortBundleNameForClass:(NSString *)name { const char *imageName = class_getImageName(NSClassFromString(name)); if (!imageName) { return @"(unspecified)"; } return [[TBRuntime runtime] shortNameForImageName:@(imageName)]; } + (NSString *)imagePathWithShortName:(NSString *)suffix { return [[TBRuntime runtime] imageNameForShortName:suffix]; } + (NSArray *)allBundleNames { return [TBRuntime runtime].imageDisplayNames; } #pragma mark Private - (NSMutableArray *)bundlePathsForToken:(TBToken *)token { // Only cache if no wildcard BOOL shouldCache = token == TBWildcardOptionsNone; if (shouldCache) { NSMutableArray *cached = [self.bundlePathsCache objectForKey:token]; if (cached) { return cached; } NSMutableArray *bundles = [[TBRuntime runtime] bundlePathsForToken:token]; [self.bundlePathsCache setObject:bundles forKey:token]; return bundles; } else { return [[TBRuntime runtime] bundlePathsForToken:token]; } } - (NSMutableArray *)bundleNamesForToken:(TBToken *)token { // Only cache if no wildcard BOOL shouldCache = token == TBWildcardOptionsNone; if (shouldCache) { NSMutableArray *cached = [self.bundleNamesCache objectForKey:token]; if (cached) { return cached; } NSMutableArray *bundles = [[TBRuntime runtime] bundleNamesForToken:token]; [self.bundleNamesCache setObject:bundles forKey:token]; return bundles; } else { return [[TBRuntime runtime] bundleNamesForToken:token]; } } - (NSMutableArray *)classesForKeyPath:(TBKeyPath *)keyPath { TBToken *classToken = keyPath.classKey; TBToken *bundleToken = keyPath.bundleKey; // Only cache if no wildcard BOOL shouldCache = bundleToken.options == 0 && classToken.options == 0; NSString *key = nil; if (shouldCache) { key = [@[bundleToken.description, classToken.description] componentsJoinedByString:@"+"]; NSMutableArray *cached = [self.classNamesCache objectForKey:key]; if (cached) { return cached; } } NSMutableArray *bundles = [self bundlePathsForToken:bundleToken]; NSMutableArray *classes = [[TBRuntime runtime] classesForToken:classToken inBundles:bundles]; if (shouldCache) { [self.classNamesCache setObject:classes forKey:key]; } return classes; } - (NSArray *> *)methodsForKeyPath:(TBKeyPath *)keyPath { // Only cache if no wildcard, but check cache anyway bc I'm lazy NSArray *cached = [self.methodsCache objectForKey:keyPath]; if (cached) { return cached; } NSArray *classes = [self classesForKeyPath:keyPath]; NSArray *> *methodLists = [[TBRuntime runtime] methodsForToken:keyPath.methodKey instance:keyPath.instanceMethods inClasses:classes ]; for (NSMutableArray *methods in methodLists) { [methods sortUsingComparator:^NSComparisonResult(FLEXMethod *m1, FLEXMethod *m2) { return [m1.description caseInsensitiveCompare:m2.description]; }]; } // Only cache if no wildcard, otherwise the cache could grow very large if (keyPath.bundleKey.isAbsolute && keyPath.classKey.isAbsolute) { [self.methodsCache setObject:methodLists forKey:keyPath]; } return methodLists; } @end