前沿
OC里的runtime就是Java里反射的概念蟹演。關于OC中runtime的應用早就被玩爛了, 今天介紹關于用發(fā)射重構代碼的例子.
首先介紹兩個函數(shù)愤诱,功能分別如下
- 獲取遵守遵守某個協(xié)議的所有類(RMClassesThatConformsToProtocol)
- 獲取繼承某個類的所有類(RMClassesThatBaseClass)
實現(xiàn)思路
獲取遵守某個協(xié)議的所有類
- 利用反射機制, 獲取所有注冊的類的個數(shù)。
int numClasses = objc_getClassList(NULL, 0);
- 根據(jù)Class類的大小, 分配對應的內存大小丐膝。
classes = (__unsafe_unretained Class*)malloc(sizeof(Class) * numClasses);
- 利用反射機制, 獲取所有的類量愧。
numClasses = objc_getClassList(classes, numClasses);
- 遍歷所有的類, 找出遵守某個協(xié)議的類加入數(shù)組
for (int index = 0; index < numClasses; index++) {
Class aClass = classes[index];
if (class_conformsToProtocol(aClass, protocol)) {
[collection addObject:aClass];
}
}
整體代碼:
NSArray<Class> *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<Class> *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行代碼, 并且每增加一個類, 懂得來手動添加相應的注冊,造成兩個問題
- 代碼很冗余帅矗,很多的重復代碼偎肃。
- 很容易忘記添加注冊。
// 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(@"沒有創(chuàng)建相應和路由對象");
}
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(@"沒有創(chuàng)建相應和路由對象");
}
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(@"沒有創(chuàng)建相應和路由對象");
}
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(@"沒有創(chuàng)建相應和路由對象");
}
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(@"沒有創(chuàng)建相應和路由對象");
}
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(@"沒有創(chuàng)建相應和路由對象");
}
return [GCDWebServerDataResponse responseWithJSONObject:json];
}];
重構背景
所有Handler對象都繼承自RMHandler, 并且RMHandler提供了start方法用于調用各個Handler對象的功能浑此,所以Handler的之類都要重寫該方法累颂,實現(xiàn)對應的功能。通過這樣構架凛俱,對于上面的代碼紊馏,就只有path參數(shù)不一致。其他幾乎是重復的代碼蒲犬。
重構思路
重構并不是一下子就能做的朱监,當你代碼越寫越多,會發(fā)現(xiàn)暖哨,上面的注冊方法大同小異赌朋,只有path參數(shù)不同,此時就該考慮重構篇裁,將這部分重復代碼抽取掉沛慢。
使用我上面提供的兩個函數(shù)功能, 由于所有的RMHandler對象,都繼承成自RMHandler达布,所以我們可以利用反射獲取所有的RMHandler對象团甲,然后提供統(tǒng)一接口,由各個類分別根據(jù)各自的功能黍聂,對接口進行重寫躺苦。重構后的代碼如下身腻,只有16行,卻實現(xiàn)了和上面一樣的功能匹厘,并且是可以很方便進行擴展嘀趟。
// 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(@"沒有創(chuàng)建相應和路由對象");
}
return [GCDWebServerDataResponse responseWithJSONObject:json];
}];
}