反射再構成コードの使用(OC)

10514 ワード

ぜんせん
OCのruntimeはJavaで反射する概念です.OCにおけるruntimeの応用はとっくにぼろぼろになっているが,今日は送信による再構成コードの例を紹介する.まず2つの関数を紹介し,機能はそれぞれ以下の通りである.
  • プロトコルに準拠するすべてのクラス(RMClassThatConformsToProtocol)
  • を取得
  • あるクラスを継承するすべてのクラス(RMClassesThatBaseClass)
  • を取得する
    実現構想.
    プロトコルを遵守するすべてのクラスを取得
  • は、反射機構を利用して、登録されたすべてのクラスの個数を取得する.int numClasses = objc_getClassList(NULL, 0);
  • Classクラスのサイズに応じて、対応するメモリサイズが割り当てられます.classes = (__unsafe_unretained Class*)malloc(sizeof(Class) * numClasses);
  • 反射機構を利用して、すべてのクラスを取得する.numClasses = objc_getClassList(classes, numClasses);
  • すべてのクラスを遍歴し、あるプロトコルを遵守するクラスを探し出して配列
  • に参加する.
     for (int index = 0; index < numClasses; index++) { 
            Class aClass = classes[index]; 
            if (class_conformsToProtocol(aClass, protocol))  { 
                      [collection addObject:aClass];
             } 
    }
    
    全体コード:
    NSArray *RMClassesThatConformsToProtocol(Protocol *protocol)
    {
        Class *classes = NULL;
        NSMutableArray *collection = [NSMutableArray array];
        int numClasses = objc_getClassList(NULL, 0);
        if (numClasses == 0 ) {
            return @[];
        }
        
        classes = (__unsafe_unretained Class*)malloc(sizeof(Class) * numClasses);
        numClasses = objc_getClassList(classes, numClasses);
        for (int index = 0; index < numClasses; index++) {
            Class aClass = classes[index];
            if (class_conformsToProtocol(aClass, protocol)) {
                [collection addObject:aClass];
            }
        }
        free(classes);
        return collection.copy;
    }
    
    クラスを継承するすべてのクラスを取得
    NSArray *RMClassesThatBaseClass(Class baseClass)
    {
        Class *classes = NULL;
        NSMutableArray *collection = [NSMutableArray array];
        int numClasses = objc_getClassList(NULL, 0);
        if (numClasses == 0 ) {
            return @[];
        }
        NSString *bcls = NSStringFromClass(baseClass);
        classes = (__unsafe_unretained Class*)malloc(sizeof(Class) * numClasses);
        numClasses = objc_getClassList(classes, numClasses);
        for (int index = 0; index < numClasses; index++) {
            Class aClass = classes[index];
            Class superClass = class_getSuperclass(aClass);
            NSString *cls = NSStringFromClass(aClass);
            if ([cls isEqualToString:bcls]) continue;
            NSString *supercls =  NSStringFromClass(superClass);
            if ([supercls isEqualToString: bcls]) {
                [collection addObject:aClass];
            }
        }
        free(classes);
        return collection.copy;
    }
    
    再構築前のコード
    再構築前には126行のコードがあり、クラスを1つ追加するたびに、対応する登録を手動で追加することがわかり、2つの問題が発生しました.
  • コードは冗長で、多くの重複コードがあります.
  • は登録の追加を忘れがちです.
  •     // register API
        [_httpServer addHandlerForMethod:@"GET" path:@"/loadDriver" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
                                        RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
                                       [handler start];
            
                                       NSMutableDictionary *json = [NSMutableDictionary dictionary];
                                       [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                                       [json setObject:request.query forKey:@"query"];
                                       return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        [_httpServer addHandlerForMethod:@"GET" path:@"/startApp" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            [json setObject:handler.sessionID forKey:kRMSessionIDKey];
            [json setObject:request.query forKey:@"query"];
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        [_httpServer addHandlerForMethod:@"GET" path:@"/startMonkey" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            [json setObject:handler.sessionID forKey:kRMSessionIDKey];
            [json setObject:request.query forKey:@"query"];
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        
        [_httpServer addHandlerForMethod:@"GET" path:@"/typeText" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            if (handler) {
                [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                [json setObject:handler.result == nil?@{}:handler.result forKey:kRMQueryKey];
            }else {
                NSLog(@"           ");
            }
            
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        
        [_httpServer addHandlerForMethod:@"GET" path:@"/findElementsByClassname" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            if (handler) {
                [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                [json setObject:handler.result == nil?@{}:handler.result forKey:kRMQueryKey];
            }else {
                NSLog(@"           ");
            }
    
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        
        
        [_httpServer addHandlerForMethod:@"GET" path:@"/clearText" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            if (handler) {
                [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                [json setObject:handler.result == nil?@{}:handler.result forKey:kRMQueryKey];
            }else {
                NSLog(@"           ");
            }
            
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        
        // tap action
        [_httpServer addHandlerForMethod:@"GET" path:@"/tap" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            if (handler) {
                [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                [json setObject:handler.result == nil?@{}:handler.result forKey:kRMQueryKey];
            }else {
                NSLog(@"           ");
            }
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        // swipe action
        [_httpServer addHandlerForMethod:@"GET" path:@"/swipe" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            if (handler) {
                [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                [json setObject:handler.result == nil?@{}:handler.result forKey:kRMQueryKey];
            }else {
                NSLog(@"           ");
            }
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
        
        
        // /inspector
        [_httpServer addHandlerForMethod:@"GET" path:@"/inspector" requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
            RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
            [handler start];
            
            NSMutableDictionary *json = [NSMutableDictionary dictionary];
            if (handler) {
                [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                [json setObject:handler.result == nil?@{}:handler.result forKey:kRMQueryKey];
            }else {
                NSLog(@"           ");
            }
            
            return [GCDWebServerDataResponse responseWithJSONObject:json];
        }];
    
    
    バックグラウンドの再構築
    すべてのHandlerオブジェクトはRMHandlerから継承され、RMHandlerはstartメソッドが各Handlerオブジェクトを呼び出す機能を提供するので、Handlerのようなメソッドは書き換えられ、対応する機能が実現される.このようにフレームワークすることで,上のコードに対してpathパラメータのみが一致しない.その他はほとんど重複するコードです.
    構想を再構築する.
    再構築はすぐにできるものではありません.コードを書けば書くほど、上記の登録方法は大きく異なり、pathパラメータだけが異なることがわかります.このとき、再構築を考えて、この重複コードを抽出しなければなりません.私が提供した2つの関数機能を使用すると、すべてのRMHandlerオブジェクトがRMHandlerから継承されるため、反射を利用してすべてのRMHandlerオブジェクトを取得し、統一インタフェースを提供し、各クラスがそれぞれの機能に基づいてインタフェースを再書き込みすることができます.再構築されたコードは以下のように,16行しかないが,上記と同様の機能を実現し,容易に拡張できる.
     // register
        NSArray *handlersClasses = RMClassesThatBaseClass(RMHandler.class);
        NSMutableArray *handlers = [NSMutableArray array];
        for (Class aClass in handlersClasses) {
            [_httpServer addHandlerForMethod:[aClass method] path:[aClass path]  requestClass:[RMRequest class] processBlock:^GCDWebServerResponse *(__kindof GCDWebServerRequest *request) {
                RMHandler *handler = [RMHandlerManager createHandlerWithRequest: request];
                [handler start];
                
                NSMutableDictionary *json = [NSMutableDictionary dictionary];
                if (handler) {
                    [json setObject:handler.sessionID forKey:kRMSessionIDKey];
                    [json setObject:handler.result == nil?@{}:handler.result forKey:kRMQueryKey];
                }else {
                    NSLog(@"           ");
                }
                return [GCDWebServerDataResponse responseWithJSONObject:json];
            }];
        }