preface
The first thing I saw was that Mushroom Street was open source and componentized through urls. MGJRouter learns the idea that all modules should expose the PCH of a file called publicHeader for other business components to call. If the home page needs to call a URL, go to the MGJRouter to find it in the publicHeader.
The principle behind all componentized communication is to expose services
- By URL: Expose a Header(expose the service that the current component has)
- CTMediator: By exposing Target(file), Category is the service we need to expose for easy invocation.
- BeeHive is injected via Runtime and Mach-o (), as well as by exposing services by loading them into arrays or memory for other modules to call.
2, MGJRouter
2.1 the registered URL
The parse URL stores Handler blocks in a dictionary into a global array that iterates through the array to find the current Block based on the current rules
3, CTMediator
Expose the Target(file) and expose the service by Category for easy invocation. CTMediator is the core call logic, which is ultimately called through the CTMediator class
Action -- SEL - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;Copy the code
The NSInvocation method invocation has the advantage of not knowing the current class or method number, The class and SEL(method number) can be obtained from the current targetName and actionName strings, and the NSInvocation argument can be set to invoke to execute the method.
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget { NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName]; // generate target NSString *targetClassString = nil; if (swiftModuleName.length > 0) { targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName]; } else { targetClassString = [NSString stringWithFormat:@"Target_%@", targetName]; } NSObject *target = self.cachedTarget[targetClassString]; If (target == nil) {// Get class class targetClass = NSClassFromString(targetClassString); // target = [[targetClass alloc] init]; } // generate action NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; SEL action = NSSelectorFromString(actionString); If (target == nil) {// If (target == nil) {// If (target == nil) {// If (target == nil) {// If (target == nil) { In practice, you can give a fixed target in advance to be used at this time, Then process this request of [self NoTargetActionResponseWithTargetString: targetClassString selectorString: actionString originParams:params]; return nil; } if (shouldCacheTarget) { self.cachedTarget[targetClassString] = target; } if ([target respondsToSelector:action]) { return [self safePerformAction:action target:target params:params]; } else {// This is where the unresponsive request is handled. If there is no response, try calling the notFound method corresponding to target to handle SEL action = NSSelectorFromString(@"notFound:"); if ([target respondsToSelector:action]) { return [self safePerformAction:action target:target params:params]; } else {// This is also where unresponsive requests are handled. If notFound is not present, this demo will return directly. In practice, you can use the fixed target above mentioned. [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params]; [self.cachedTarget removeObjectForKey:targetClassString]; return nil; }}}Copy the code
The corresponding method is executed via NSInvocation
// NSInvocation invocation - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params { NSMethodSignature* methodSig = [target methodSignatureForSelector:action]; if(methodSig == nil) { return nil; } const char* retType = [methodSig methodReturnType]; if (strcmp(retType, @encode(void)) == 0) {//NSInvocation * Invocation = [NSInvocation * Invocation] invocationWithMethodSignature:methodSig]; [invocation setArgument:¶ms atIndex:2]; [invocation setSelector:action]; [invocation setTarget:target]; // The corresponding method executes the [Invocation invoke]; return nil; } if (strcmp(retType, @encode(NSInteger)) == 0) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; [invocation setArgument:¶ms atIndex:2]; [invocation setSelector:action]; [invocation setTarget:target]; [invocation invoke]; NSInteger result = 0; [invocation getReturnValue:&result]; return @(result); } if (strcmp(retType, @encode(BOOL)) == 0) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; [invocation setArgument:¶ms atIndex:2]; [invocation setSelector:action]; [invocation setTarget:target]; [invocation invoke]; BOOL result = 0; [invocation getReturnValue:&result]; return @(result); } if (strcmp(retType, @encode(CGFloat)) == 0) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; [invocation setArgument:¶ms atIndex:2]; [invocation setSelector:action]; [invocation setTarget:target]; [invocation invoke]; CGFloat result = 0; [invocation getReturnValue:&result]; return @(result); } if (strcmp(retType, @encode(NSUInteger)) == 0) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; [invocation setArgument:¶ms 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 }Copy the code
Using CTMediator
- 1. Each module needs a corresponding
Target
File to expose the services of the current module. - 2. One needs to be maintained
CTMediator
A categorized Pod library to serve the current module. For other modules to call. - Other module calls simply rely on the exposed Pod library.
CTMediator+CTMediatorModuleAActions is intended to solve the problem of hard coding.
Call to order
CTMediator+CTMediatorModuleAActions
===> CTMediator
===> Target_A
===> DemoModuleADetailViewController
4, BeeHive
-
Called as a BHAnnotation annotation (injected through Macho).
-
BHAppDelegate overrides the AppDelegate logic
-
BHConfig Context configuration environment
-
BHModuleManager module management (home page and personal Center modules)
-
Management of BHServiceManager services (per module exposed method)
-
BHContext distributes the environment inside of it
BHContext maintains a servicesByName dictionary that stores protocols and services (IMP implementations) in key-value form.
-
Generation decoupling (taking over the APP Delegate) is distributed to different modules.
-
Decoupling (de-coupling) between modules
4.1 Service Registration
4.1.1 MachO dependency injection registry service
Register the current Beehive vices as a section field and Beehive vices as a section name to Macho’s __DATA data segment. The BeehiveServices field stores the HomeProtocol and BHViewController.
__DATA consists of different sections, each of which has a different section name __attribute GNU compiled syntax
//used tells the compiler what functions to keep: // #define BeeHiveDATA(sectName) __attribute((used, section("__DATA,"#sectname" "))) #define BeeHiveMod(name) \ class BeeHive; char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name""; // @beehiveservice (HomeServiceProtocol, BHViewController) //HomeProtocol #define BeeHiveService(servicename,impl) \ class BeeHive; char * k##servicename##_service BeeHiveDATA(BeehiveServices) = "{ \""#servicename"\" : \""#impl"\"}"; /** char *kHomeProtocol_service __attribute((used, section("__DATA,"#BeehiveServices" "))) = {\"""HomeProtocol"""\,\""""BHViewController"""\} */Copy the code
5. Module division
Modules: (Home page module, Personal Center module) Services: (Each module exposed method)
- Modules: Made up of different components
- Components:
LGUtils
、LGNetwork
Division of APP responsibilities (up to three layers)
The underlying
: Base layer (network request, data cache)Generic business layer
: Get data === => Clean data === => Deliver data, play video (separate Pod library)Specific business layer
: Home module Concern module
Depend on the sink
- Dependencies should be unitary
- The upper can only depend on the lower
- The lower layer should not know the business logic of the upper layer, and if there is such a function, it needs to be separately extracted and sunk into the lower module