iOS架构设计与优化(二)

上篇提到利用数据分层思想来解耦项目代码和逻辑,数据分层一个很重要的工具就是Service类,这篇就重点介绍Service的实现和属性的动态装配

服务类的注册实现

要实现某个服务,本质就是实现一套协议方法,其他语言中协议方法也叫做一套接口,还是拿之前的数据获取服务举例:

@interface LYIPAggregationBusinessDatas : NSObject <ExportableProtocol, LYIPAggregationBusinessDatasProtocol>

@end

@implementation LYIPAggregationBusinessDatas

LY_EXPORT_SERVICE_FOR_PROTOCOL(LYIPAggregationBusinessDatasProtocol, NO);

- (LYSignal<NSArray<LYAggregationMenuItemDetail *> *> *)allMenuItems {
    
    return [[@"/v1/guest/goodsCategory".http_get parameters:nil]
            resultMap:^id(id value) {
                return [LYAggregationMenuItemDetail ly_objectArrayWithKeyValuesArray:value];
            }].signal;
}
@end

服务类的注册是通过一个宏来实现

LY_EXPORT_SERVICE_FOR_PROTOCOL(LYIPAggregationBusinessDatasProtocol, NO);

跳到实现

#define MC_SERVICE(_protocol_, _isSingleton_) \
        + (void)load {\
            [[MCModuleConnector shared] registerServiceForProtocol:@protocol(_protocol_) clazz:self singleton:_isSingleton_];\
        }

继续

BOOL checkConfirmProtocolIncludingSuperClass(Class aClass, Protocol *aProtocol) {
    if (!aClass) {
        return NO;
    }
    if (!class_conformsToProtocol(aClass, aProtocol)) {
        return checkConfirmProtocolIncludingSuperClass(class_getSuperclass(aClass), aProtocol);
    }
    return YES;
}

- (void)registerServiceForProtocol:(Protocol *)aProtocol clazz:(Class<MCExportableProtocol>)clazz singleton:(BOOL)singleton {
    [self addOperationWithBlock:^{
        if (!checkConfirmProtocolIncludingSuperClass(clazz, @protocol(MCExportableProtocol))) {
            NSAssert(NO, @"类【%@】需要实现 ExportableProtocol", NSStringFromClass(clazz));
        }
        MCService *service = _services[NSStringFromProtocol(aProtocol)];

        NSAssert(!service, @"Service for protocol: %@ exists, can not register again", NSStringFromProtocol(aProtocol));

        service = [MCService serviceWithClass:clazz];
        service.singleton = singleton;

        _services[NSStringFromProtocol(aProtocol)] = service;
    }];
}

在注册时,可以先通过runtime提供的API判断注册的类是否遵守了协议方法来保证安全性,注册时需要传入协议方法名protocol和注册类class作为参数传入服务工具类中,在工具类的内部用hashMap的形式,将protocol的名称作为key,由于对是服务类否为单例做了一层封装,所以没有直接将class名称作为value,而是将封装后的MCService对象作为value存入字典。为了保证性能,这一系列的操作都放入子线程中处理

服务类的获取实现

先看服务类获取代码

- (id)serviceWithProtocol:(Protocol *)aProtocol {
    __block id<MCExportableProtocol> service = nil;
    [self addOperationWithBlock:^{
        service = _services[NSStringFromProtocol(aProtocol)].getService;
    }];
    return service;
}

看下MCService代码

- (id<MCExportableProtocol>)getService {
    return _singleton ? self.singletonService : self.generateService;
}

- (id<MCExportableProtocol>)singletonService {
    @synchronized (self) {
        if (!_singletonService) {
            _singletonService = [self generateService];
        }
    }
    return _singletonService;
}

- (id<MCExportableProtocol>)generateService {
    id service = [_serviceClass ep_defaultInstance]?:[[((Class)_serviceClass) alloc] init];
    [service mc_autowire];
    return service;
}

若自己实现了单例,则返回单例方法,否则跟根据注册的类创建实例对象。由于此对象实现了protocol的方法,在使用时就可以直接调用借口方法。至此,大致就能实现了服务类的注册和获取,完成了层级之间的屏蔽,各层之间不用关心具体实现类,直接使用,从而达到数据分层

属性的自动赋值

为了方便在服务类作为属性时使用,获得更高的动态性,提供了一种动态获取和自动赋值的方式来自动获取注册的服务类的方式

先看使用:

@property (nonatomic, strong) id <LYIPAggregationBusinessProtocol, MCAutowired>dataGetter;

由于实现了自动和动态赋值,所以在使用时就十分简单,一行代码即可。括号中包含两个协议,前面协议为服务实现的接口协议,后面一个协议为MCAutowired自动赋值协议,为了实现所有类都能一行代码自动赋值,所以添加一个NSObject的分类来实现MCAutowired协议

@implementation NSObject (MCServiceAutowired)

//static NSRegularExpression *_reg = nil;

- (void)autowired {
    [self mc_autowire];
}

- (void)mc_autowire {
    NSError *error;
    NSRegularExpression *reg = [[NSRegularExpression alloc] initWithPattern:@"\\b\\w+\\b" options:NSRegularExpressionCaseInsensitive error:&error];
    [self autowiredForClass:self.class withReg:reg];
}


- (void)autowiredForClass:(Class)aClass withReg:(NSRegularExpression *)reg {
    unsigned int outCount = 0;
    Ivar *list = class_copyIvarList(aClass, &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = list[i];
        NSString *typeEncoding = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
//        NSLog(@"---%@", typeEncoding);
        if ([typeEncoding hasPrefix:@"@\"<"]) {
            NSArray<NSTextCheckingResult *> *result = [reg matchesInString:typeEncoding options:NSMatchingReportProgress range:NSMakeRange(0, typeEncoding.length)];
            if (result.count == 2) {

                // 判断是否标记为 MCAutowired
                if (![[typeEncoding substringWithRange:result.lastObject.range] isEqualToString:NSStringFromProtocol(@protocol(MCAutowired))]) {
                    continue;
                }

                NSString *protocolName = [typeEncoding substringWithRange:result.firstObject.range];

                NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];

                @try {
                    [self setValue:[[MCModuleConnector shared] serviceWithProtocol:objc_getProtocol(protocolName.UTF8String)]  forKey:ivarName];
                } @catch (NSException *exception) {
                    NSLog(@"%@", exception);
                } @finally {

                }
            }
        }
    }
    free(list);
    Class superClass = class_getSuperclass(aClass);
    if (!superClass) return;
    
    [self autowiredForClass:superClass withReg:reg];
}
@end

核心思想为利用runtime动态判断属性是否标记为MCAutowired自动赋值,若已经标记,则去服务工具类中获取注册的实现类来为属性赋值

至此,在OC中就提供了一套动态注册和获取的方式来实现数据分层,解耦。实现解耦的方式还有很多,可能这里只是提供了一种思路和解决方案,希望能给大家有所启发