iOS組件化(二)

本文主要介紹組件化通訊的三種方式

URL路由 ---- MGJRouter

基于URL匹配或者命名約定击胜,通過runtime動(dòng)態(tài)調(diào)用捌浩,實(shí)現(xiàn)簡單快集,類似系統(tǒng)的openURL

優(yōu)點(diǎn)
  • 極高的動(dòng)態(tài)性
  • 可以統(tǒng)一管理多平臺的路由規(guī)則
  • 可以適配 URL Scheme
缺點(diǎn)
  • 參數(shù)通過字符串形式轉(zhuǎn)換,方式有限葵袭,編譯期無法進(jìn)行類型檢測
  • 參數(shù)格式不明確
  • 不支持Storyboard
  • 無法保證使用模塊一定存在
  • 解耦能力有限涵妥,url字符串一旦修改會導(dǎo)致其他地方失效

MGJRouter

  • App啟動(dòng)時(shí)實(shí)例化所需的組件模塊,注冊url
  • 需要調(diào)用其他模塊時(shí)坡锡,傳遞url和參數(shù)蓬网,類似系統(tǒng)的openURL
//注冊url
[MGJRouter registerURLPattern:@"mgj://category/travel" toHandler:^(NSDictionary *routerParameters) {
        NSLog(@"routerParameters == %@",routerParameters);
}];
    
//調(diào)用路由
[MGJRouter openURL:@"mgj://category/travel" withUserInfo:@{@"user_id": @1900} completion:nil];

Target-Action -- CTMediator

基于runtime和category特性,動(dòng)態(tài)獲取模塊鹉勒,例如通過NSClassFromString獲取實(shí)例帆锋,performSelector + NSInvocation動(dòng)態(tài)調(diào)用方法

  • 利用分類添加路由接口,通過字符串獲取對應(yīng)的類
  • 通過runtime創(chuàng)建實(shí)例禽额,動(dòng)態(tài)調(diào)用方法
優(yōu)點(diǎn)
  • 通過category明確接口聲明
  • 輕量
缺點(diǎn)
  • 模塊化繁瑣窟坐,需要在target、mediator重新添加每個(gè)接口
  • category中通過字符串識別绵疲,存在和URL路由一樣的問題
  • 無法保證使用的模塊一定存在,target修改后臣疑,運(yùn)行時(shí)才能報(bào)錯(cuò)

CTMediator

在分類中通過調(diào)用performTarget:action:params:shouldCacheTarget:方法盔憨,找到對應(yīng)的targetaction

- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
    if (targetName == nil || actionName == nil) {
        return nil;
    }
    //在swift中使用時(shí),需要傳入對應(yīng)項(xiàng)目的target名稱讯沈,否則會找不到視圖控制器
    NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
    
    // generate target 生成target
    NSString *targetClassString = nil;
    if (swiftModuleName.length > 0) {
        //swift中target文件名拼接
        targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
    } else {
        //OC中target文件名拼接
        targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    }
    //緩存中查找target
    NSObject *target = [self safeFetchCachedTarget:targetClassString];
    //緩存中沒有target
    if (target == nil) {
        //通過字符串獲取對應(yīng)的類
        Class targetClass = NSClassFromString(targetClassString);
        //創(chuàng)建實(shí)例
        target = [[targetClass alloc] init];
    }

    // generate action 生成action方法名稱
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
    //通過方法名字符串獲取對應(yīng)的sel
    SEL action = NSSelectorFromString(actionString);
    
    if (target == nil) {
        // 這里是處理無響應(yīng)請求的地方之一郁岩,這個(gè)demo做得比較簡單,如果沒有可以響應(yīng)的target缺狠,就直接return了问慎。實(shí)際開發(fā)過程中是可以事先給一個(gè)固定的target專門用于在這個(gè)時(shí)候頂上,然后處理這種請求的
        [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
        return nil;
    }
    //是否需要緩存
    if (shouldCacheTarget) {
        [self safeSetCachedTarget:target key:targetClassString];
    }
    //是否響應(yīng)sel
    if ([target respondsToSelector:action]) {
        //動(dòng)態(tài)調(diào)用方法
        return [self safePerformAction:action target:target params:params];
    } else {
        // 這里是處理無響應(yīng)請求的地方挤茄,如果無響應(yīng)如叼,則嘗試調(diào)用對應(yīng)target的notFound方法統(tǒng)一處理
        SEL action = NSSelectorFromString(@"notFound:");
        if ([target respondsToSelector:action]) {
            return [self safePerformAction:action target:target params:params];
        } else {
            // 這里也是處理無響應(yīng)請求的地方,在notFound都沒有的時(shí)候穷劈,這個(gè)demo是直接return了笼恰。實(shí)際開發(fā)過程中踊沸,可以用前面提到的固定的target頂上的。
            [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
            @synchronized (self) {
                [self.cachedTarget removeObjectForKey:targetClassString];
            }
            return nil;
        }
    }
}
  • safePerformAction:target:params:方法的實(shí)現(xiàn)社证,是通過NSInvocation進(jìn)行消息轉(zhuǎn)發(fā)+參數(shù)傳遞
- (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
{
    //獲取方法簽名
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    if(methodSig == nil) {
        return nil;
    }
    //判斷返回類型逼龟,根據(jù)返回值完成參數(shù)傳遞
    const char* retType = [methodSig methodReturnType];
    //void類型
    if (strcmp(retType, @encode(void)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        return nil;
    }
    //NSInteger類型
    if (strcmp(retType, @encode(NSInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
    //BOOL類型
    if (strcmp(retType, @encode(BOOL)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        BOOL result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
    //CGFloat類型
    if (strcmp(retType, @encode(CGFloat)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        CGFloat result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
    //NSUInteger類型
    if (strcmp(retType, @encode(NSUInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSUInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}

Protocol Class -- BeeHive

  • protocol和對應(yīng)的字典匹配
  • 通過protocol獲取class動(dòng)態(tài)創(chuàng)建
優(yōu)點(diǎn)
  • 通過接口調(diào)用,保證參數(shù)類型安全
  • 直接使用protocol接口追葡,無需重復(fù)封裝
缺點(diǎn)
  • 不支持swift
  • 無法保證使用的Protocol一定存在對應(yīng)模塊
  • 通過框架創(chuàng)建所有對象腺律,不支持外部傳入?yún)?shù)

BeeHive

BeeHive借鑒了Spring Service、Apache DSO的架構(gòu)理念宜肉,采用AOP+擴(kuò)展Application API形式匀钧,將業(yè)務(wù)基礎(chǔ)功能通過模塊方法解耦,模塊直接通過Service形式調(diào)用崖飘,避免直接依賴榴捡,在AppDelegate中解耦,進(jìn)行邏輯拆分朱浴,每個(gè)模塊微應(yīng)用形式獨(dú)立存在

BeeHive 注冊

BeeHive通過BHModuleManager來管理已經(jīng)被注冊的模塊
BeeHive提供了三種注冊方法:.plist吊圾、動(dòng)態(tài)注冊(load)Annotation

.plist文件
  • 設(shè)置plist文件路徑
[BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";//可選翰蠢,默認(rèn)為BeeHive.bundle/BeeHive.plist
  • 創(chuàng)建plist文件

    image.png

  • loadLocalModules方法讀取plist文件项乒,添加到BHModuleInfos數(shù)組

#pragma mark - Private
//初始化context,加載Modules梁沧、Services
-(void)setContext:(BHContext *)context
{
    _context = context;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self loadStaticServices];
        [self loadStaticModules];
    });
}

//加載modules
- (void)loadStaticModules
{
    // 讀取本地plist文件里面的Module檀何,并注冊到BHModuleManager的BHModuleInfos數(shù)組中
    [[BHModuleManager sharedManager] loadLocalModules];
    //注冊所有modules,在內(nèi)部根據(jù)優(yōu)先級進(jìn)行排序
    [[BHModuleManager sharedManager] registedAllModules];   
}

- (void)loadLocalModules
{
    //plist文件路徑
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:[BHContext shareInstance].moduleConfigName ofType:@"plist"];
    //判斷文件是否存在
    if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
        return;
    }
    //讀取整個(gè)文件[@"moduleClasses" : 數(shù)組]
    NSDictionary *moduleList = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
    //通過moduleClasses key讀取 數(shù)組 [[@"moduleClass":"aaa", @"moduleLevel": @"bbb"], [...]]
    NSArray<NSDictionary *> *modulesArray = [moduleList objectForKey:kModuleArrayKey];
    NSMutableDictionary<NSString *, NSNumber *> *moduleInfoByClass = @{}.mutableCopy;
    //遍歷數(shù)組
    [self.BHModuleInfos enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [moduleInfoByClass setObject:@1 forKey:[obj objectForKey:kModuleInfoNameKey]];
    }];
    [modulesArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (!moduleInfoByClass[[obj objectForKey:kModuleInfoNameKey]]) {
            //存儲到 BHModuleInfos 中
            [self.BHModuleInfos addObject:obj];
        }
    }];
}

動(dòng)態(tài)注冊(load)
  • load方法中注冊
+ (void)load
{
    [BeeHive registerDynamicModule:[self class]];
}
  • registerDynamicModule方法
+ (void)registerDynamicModule:(Class)moduleClass
{
    [[BHModuleManager sharedManager] registerDynamicModule:moduleClass];
}

- (void)registerDynamicModule:(Class)moduleClass
{
    [self registerDynamicModule:moduleClass shouldTriggerInitEvent:NO];
}

- (void)registerDynamicModule:(Class)moduleClass
       shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
{
    [self addModuleFromObject:moduleClass shouldTriggerInitEvent:shouldTriggerInitEvent];
}
  • 或者通過宏定義BH_EXPORT_MODULE代替load方法
#define BH_EXPORT_MODULE(isAsync) \
+ (void)load { [BeeHive registerDynamicModule:[self class]]; } \
-(BOOL)async { return [[NSString stringWithUTF8String:#isAsync] boolValue];}
Annotation注冊
  • 通過BeeHiveMod的宏進(jìn)行Annotation標(biāo)記
//***** 使用
BeeHiveMod(ShopModule)

//***** BeeHiveMod的宏定義
#define BeeHiveMod(name) \
class BeeHive; char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name"";

//***** BeeHiveDATA的宏定義 
#define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname" ")))

//*****  全部轉(zhuǎn)換出來后為下面的格式
char * kShopModule_mod __attribute((used, section("__DATA,""BeehiveMods"" "))) = """ShopModule""";

__attribute

  • 第一個(gè)參數(shù)used:用來修飾函數(shù)廷支,被修飾后频鉴,意味著即使函數(shù)沒有被引用,在Release下也不會被優(yōu)化

  • section("__DATA,""BeehiveMods"" "):指定未被優(yōu)化的模塊注入Mach-o__DATA

  • BHReadConfiguration方法讀取Mach-o中的數(shù)據(jù)段恋拍,取出存放到數(shù)組

NSArray<NSString *>* BHReadConfiguration(char *sectionName,const struct mach_header *mhp)
{
    
    NSMutableArray *configs = [NSMutableArray array];
    unsigned long size = 0;
#ifndef __LP64__
    // 找到之前存儲的數(shù)據(jù)段(Module找BeehiveMods段 和 Service找BeehiveServices段)的一片內(nèi)存
    uintptr_t *memory = (uintptr_t*)getsectiondata(mhp, SEG_DATA, sectionName, &size);
#else
    const struct mach_header_64 *mhp64 = (const struct mach_header_64 *)mhp;
    uintptr_t *memory = (uintptr_t*)getsectiondata(mhp64, SEG_DATA, sectionName, &size);
#endif
    
    unsigned long counter = size/sizeof(void*);
    // 把特殊段里面的數(shù)據(jù)都轉(zhuǎn)換成字符串存入數(shù)組中
    for(int idx = 0; idx < counter; ++idx){
        char *string = (char*)memory[idx];
        NSString *str = [NSString stringWithUTF8String:string];
        if(!str)continue;
        
        BHLog(@"config = %@", str);
        if(str) [configs addObject:str];
    }
    
    return configs; 
}

BeeHive模塊事件

BeeHive給每個(gè)模塊提供Application生命周期事件垛孔,用于與主程序進(jìn)行必要信息交互,感知模塊生命周期的變化施敢。
所有的Module必須遵守BHModuleProtocol協(xié)議周荐,否則無法接受事件消息

BHModuleManager中,所有時(shí)間被定義成枚舉BHModuleEventType

typedef NS_ENUM(NSInteger, BHModuleEventType)
{
    //設(shè)置Module模塊
    BHMSetupEvent = 0,
    //用于初始化Module模塊僵娃,例如環(huán)境判斷概作,根據(jù)不同環(huán)境進(jìn)行不同初始化
    BHMInitEvent,
    //用于拆除Module模塊
    BHMTearDownEvent,
    BHMSplashEvent,
    BHMQuickActionEvent,
    BHMWillResignActiveEvent,
    BHMDidEnterBackgroundEvent,
    BHMWillEnterForegroundEvent,
    BHMDidBecomeActiveEvent,
    BHMWillTerminateEvent,
    BHMUnmountEvent,
    BHMOpenURLEvent,
    BHMDidReceiveMemoryWarningEvent,
    BHMDidFailToRegisterForRemoteNotificationsEvent,
    BHMDidRegisterForRemoteNotificationsEvent,
    BHMDidReceiveRemoteNotificationEvent,
    BHMDidReceiveLocalNotificationEvent,
    BHMWillPresentNotificationEvent,
    BHMDidReceiveNotificationResponseEvent,
    BHMWillContinueUserActivityEvent,
    BHMContinueUserActivityEvent,
    BHMDidFailToContinueUserActivityEvent,
    BHMDidUpdateUserActivityEvent,
    BHMHandleWatchKitExtensionRequestEvent,
    BHMDidCustomEvent = 1000
    
};
  • 系統(tǒng)事件:Application生命周期
    通過將項(xiàng)目的AppDelegate繼承BHAppDelegate
@interface TestAppDelegate : BHAppDelegate <UIApplicationDelegate>
系統(tǒng)事件
  • 應(yīng)用事件:官方給出的流程圖,其中modSetup默怨、modInit等讯榕,可以用于編碼實(shí)現(xiàn)各插件模塊的設(shè)置與初始化。

    應(yīng)用事件

  • 自定義事件:可以通過調(diào)用BHModuleManagertriggerEvent :方法處理所有事件

- (void)triggerEvent:(NSInteger)eventType
{
    [self triggerEvent:eventType withCustomParam:nil];
    
}

- (void)triggerEvent:(NSInteger)eventType
     withCustomParam:(NSDictionary *)customParam {
    [self handleModuleEvent:eventType forTarget:nil withCustomParam:customParam];
}

#pragma mark - module protocol
- (void)handleModuleEvent:(NSInteger)eventType
                forTarget:(id<BHModuleProtocol>)target
          withCustomParam:(NSDictionary *)customParam
{
    switch (eventType) {
            //初始化事件
        case BHMInitEvent:
            //special
            [self handleModulesInitEventForTarget:nil withCustomParam :customParam];
            break;
            //析構(gòu)事件
        case BHMTearDownEvent:
            //special
            [self handleModulesTearDownEventForTarget:nil withCustomParam:customParam];
            break;
            //其他3類事件
        default: {
            NSString *selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
            [self handleModuleEvent:eventType forTarget:nil withSeletorStr:selectorStr andCustomParam:customParam];
        }
            break;
    }
    
}

從上面代碼發(fā)現(xiàn)匙睹,除了BHMInitEvent初始化瘩扼、BHMTearDownEvent析構(gòu)兩個(gè)特殊事件外谆甜,所有的事件都是調(diào)用handleModuleEvent:forTarget:withSeletorStr:andCustomParam:,在內(nèi)部通過遍歷moduleInstances數(shù)組集绰,調(diào)用performSelector:withObject:實(shí)現(xiàn)

- (void)handleModuleEvent:(NSInteger)eventType
                forTarget:(id<BHModuleProtocol>)target
           withSeletorStr:(NSString *)selectorStr
           andCustomParam:(NSDictionary *)customParam
{
    BHContext *context = [BHContext shareInstance].copy;
    context.customParam = customParam;
    context.customEvent = eventType;
    if (!selectorStr.length) {
        selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
    }
    SEL seletor = NSSelectorFromString(selectorStr);
    if (!seletor) {
        selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
        seletor = NSSelectorFromString(selectorStr);
    }
    NSArray<id<BHModuleProtocol>> *moduleInstances;
    if (target) {
        moduleInstances = @[target];
    } else {
        moduleInstances = [self.BHModulesByEvent objectForKey:@(eventType)];
    }
    //遍歷 moduleInstances 實(shí)例數(shù)組规辱,調(diào)用performSelector:withObject:方法實(shí)現(xiàn)對應(yīng)方法調(diào)用
    [moduleInstances enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([moduleInstance respondsToSelector:seletor]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            //進(jìn)行方法調(diào)用
            [moduleInstance performSelector:seletor withObject:context];
#pragma clang diagnostic pop
            
            [[BHTimeProfiler sharedTimeProfiler] recordEventTime:[NSString stringWithFormat:@"%@ --- %@", [moduleInstance class], NSStringFromSelector(seletor)]];
            
        }
    }];
}

3、BeeHive模塊調(diào)用

通過BHServiceManager來管理已經(jīng)注冊的Protocol
注冊Protocol的方式和注冊Module方式一一對應(yīng)

.plist文件
  • 設(shè)置plist文件路徑
[BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";
  • 創(chuàng)建plist文件
    image.png
  • loadStaticServices方法讀取plist文件栽燕,存儲到字典中
#pragma mark - Private
//初始化context罕袋,加載Modules、Services
-(void)setContext:(BHContext *)context
{
    _context = context;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self loadStaticServices];
        [self loadStaticModules];
    });
}

//加載Services
-(void)loadStaticServices
{
    [BHServiceManager sharedManager].enableException = self.enableException;
    
    [[BHServiceManager sharedManager] registerLocalServices];
    
}

- (void)registerLocalServices
{
    NSString *serviceConfigName = [BHContext shareInstance].serviceConfigName;
    //plist文件路徑
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:serviceConfigName ofType:@"plist"];
    if (!plistPath) {
        return;
    }
    
    NSArray *serviceList = [[NSArray alloc] initWithContentsOfFile:plistPath];
    
    [self.lock lock];
    //遍歷并存儲到allServicesDict中
    for (NSDictionary *dict in serviceList) {
        NSString *protocolKey = [dict objectForKey:@"service"];
        NSString *protocolImplClass = [dict objectForKey:@"impl"];
        if (protocolKey.length > 0 && protocolImplClass.length > 0) {
            [self.allServicesDict addEntriesFromDictionary:@{protocolKey:protocolImplClass}];
        }
    }
    [self.lock unlock];
}


動(dòng)態(tài)注冊(load)
  • load方法中注冊Protocol協(xié)議碍岔,通過調(diào)用BeeHive里面的registerService:service:方法完成注冊
+ (void)load
{
   [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]];
}
??
- (void)registerService:(Protocol *)proto service:(Class) serviceClass
{
    [[BHServiceManager sharedManager] registerService:proto implClass:serviceClass];
}
Annotation注冊
//****** 1浴讯、通過BeeHiveService宏進(jìn)行Annotation標(biāo)記
BeeHiveService(HomeServiceProtocol,BHViewController)

//****** 2、宏定義
#define BeeHiveService(servicename,impl) \
class BeeHive; char * k##servicename##_service BeeHiveDATA(BeehiveServices) = "{ \""#servicename"\" : \""#impl"\"}";

//****** 3蔼啦、轉(zhuǎn)換后的格式榆纽,也是將其存儲到特殊的段
char * kHomeServiceProtocol_service __attribute((used, section("__DATA,""BeehiveServices"" "))) = "{ \"""HomeServiceProtocol""\" : \"""BHViewController""\"}";

獲取Protocol

Protocol相比Module,可以返回Protocol實(shí)例對象

- (id)createService:(Protocol *)proto;
{
    return [[BHServiceManager sharedManager] createService:proto];
}
??
- (id)createService:(Protocol *)service
{
    return [self createService:service withServiceName:nil];
}
??
- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName {
    return [self createService:service withServiceName:serviceName shouldCache:YES];
}
??
- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache {
    if (!serviceName.length) {
        serviceName = NSStringFromProtocol(service);
    }
    id implInstance = nil;
    //判斷protocol是否已經(jīng)注冊過
    if (![self checkValidService:service]) {
        if (self.enableException) {
            @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol does not been registed", NSStringFromProtocol(service)] userInfo:nil];
        }
        
    }
    
    NSString *serviceStr = serviceName;
    //如果有緩存捏肢,則直接從緩存中獲取
    if (shouldCache) {
        id protocolImpl = [[BHContext shareInstance] getServiceInstanceFromServiceName:serviceStr];
        if (protocolImpl) {
            return protocolImpl;
        }
    }
    //獲取類后奈籽,然后響應(yīng)下層的方法
    Class implClass = [self serviceImplClass:service];
    if ([[implClass class] respondsToSelector:@selector(singleton)]) {
        if ([[implClass class] singleton]) {
            if ([[implClass class] respondsToSelector:@selector(shareInstance)])
                //創(chuàng)建單例對象
                implInstance = [[implClass class] shareInstance];
            else
                //創(chuàng)建實(shí)例對象
                implInstance = [[implClass alloc] init];
            if (shouldCache) {
                //緩存
                [[BHContext shareInstance] addServiceWithImplInstance:implInstance serviceName:serviceStr];
                return implInstance;
            } else {
                return implInstance;
            }
        }
    }
    return [[implClass alloc] init];
}
  • 創(chuàng)建service之前,會檢查Protocol是否注冊過

  • 從字典中取出Protocol對應(yīng)的Class

  • 如果實(shí)現(xiàn)了singleton方法

  • 實(shí)現(xiàn)了shareInstance鸵赫,創(chuàng)建單例

  • 否則創(chuàng)建實(shí)例對象

  • implInstanceserviceStr緩存

  • 創(chuàng)建實(shí)例對象

  • serviceImplClass方法中衣屏,protocol為key,serviceImp為value存在字典中

- (Class)serviceImplClass:(Protocol *)service
{
    //通過字典將 協(xié)議 和 類 綁定辩棒,其中協(xié)議作為key狼忱,serviceImp(類的名字)作為value
    NSString *serviceImpl = [[self servicesDict] objectForKey:NSStringFromProtocol(service)];
    if (serviceImpl.length > 0) {
        return NSClassFromString(serviceImpl);
    }
    return nil;
}

輔助類

  • BHConfig:單例,維護(hù)一些動(dòng)態(tài)的環(huán)境變量一睁,作為BHContext的補(bǔ)充

  • BHContext:單例钻弄,內(nèi)部兩個(gè)字典屬性:modulesByNameservicesByName者吁,用來保存上下文信息

  • BHTimeProfiler:計(jì)算時(shí)間性能方面的

  • BHWatchDog:新開一個(gè)線程斧蜕,監(jiān)聽主線程是否堵塞

參考鏈接

BeeHive —— 一個(gè)優(yōu)雅但還在完善中的解耦框架

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市砚偶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洒闸,老刑警劉巖染坯,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異丘逸,居然都是意外死亡单鹿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門深纲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仲锄,“玉大人劲妙,你說我怎么就攤上這事∪搴埃” “怎么了镣奋?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長怀愧。 經(jīng)常有香客問我侨颈,道長,這世上最難降的妖魔是什么芯义? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任哈垢,我火速辦了婚禮,結(jié)果婚禮上扛拨,老公的妹妹穿的比我還像新娘耘分。我一直安慰自己,他們只是感情好绑警,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布求泰。 她就那樣靜靜地躺著,像睡著了一般待秃。 火紅的嫁衣襯著肌膚如雪拜秧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天章郁,我揣著相機(jī)與錄音枉氮,去河邊找鬼。 笑死暖庄,一個(gè)胖子當(dāng)著我的面吹牛聊替,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播培廓,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惹悄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肩钠?” 一聲冷哼從身側(cè)響起泣港,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎价匠,沒想到半個(gè)月后当纱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踩窖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年坡氯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡箫柳,死狀恐怖手形,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悯恍,我是刑警寧澤库糠,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站坪稽,受9級特大地震影響曼玩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窒百,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一黍判、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧篙梢,春花似錦顷帖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至妄呕,卻和暖如春陶舞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绪励。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工肿孵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疏魏。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓停做,卻偏偏與公主長得像,于是被迫代替她去往敵國和親大莫。 傳聞我的和親對象是個(gè)殘疾皇子蛉腌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容