前沿
OC里的runtime就是Java里反射的概念。关于OC中runtime的应用早就被玩烂了, 今天介绍关于用发射重构代码的例子.
首先介绍两个函数,功能分别如下
- 获取遵守遵守某个协议的所有类(RMClassesThatConformsToProtocol)
- 获取继承某个类的所有类(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<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(@"没有创建相应和路由对象");
}
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参数不同,此时就该考虑重构,将这部分重复代码抽取掉。
使用我上面提供的两个函数功能, 由于所有的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];
}];
}