Because business needs, so have this reading action
Basic class functionality concepts
-
An overview of classes within the framework
-
BHModule:
- Classes that listen for system-level events and execute them (that is, classes that handle APP life cycle events and related system layers)
BHModule
Need to followBHModuleProtocol
The system-level event interface is defined inBHModuleProtocol
)- By itself
alloc init
Mode self-initializes modules when not presentBHModuleManager
Under the management
-
BHContext:
- Manages the application environment, global configuration, and event-triggering context objects (that is, context parameters for event methods)
- Also includes
BHShortcutItem
,BHOpenURLItem
,BHNotificationsItem
,BHUserActivityItem
,BHWatchItem
-
BHModuleManager: Responsible for [registering] the BHModule and managing it
BHModules
Array:- Stores registered BHModule instances (of each class)
BHModule
There can only be one instance, and filtering is done during registration,isKindOfClass
Is no longer counted in this instance.) - The sorting
- According to the first
basicModuleLeve
(From small to large, basic < normal) - If the front is equal, pass
modulePriority
Row (from large to small)
- According to the first
- Stores registered BHModule instances (of each class)
BHModuleInfos
Dictionary array: dictionary of module information created when registering a module, including module class, module level, whether module has been Instantiated, etcBHSelectorByEvent
Event table dictionary: Manages default system event types and custom event typesBHModulesByEvent
Event-response instance array table dictionary: module instances that will be able to respond to events of the same typeBHModules
The sorting method of an array is stored in the corresponding array
-
BHServiceManager: Manages each registered Protocol
Module registration (In the order executed at startup )
1.Annotation method registration
- Function corresponding classes:
BHAnnotation
- By using the macro Beehive emod (
The name of the class
Writes the class name string to the memory section of the specified name at compile time
Using this macro in ShopModule allows you to write the ShopModule class name to the __DATA section at compile time with BeehiveMods as the key location
The result of macro translation is
- The macro modifier method is __attribute__((constructor))
void initProphet() {... }
, make the methodIs called before main
The _dyLD_register_func_FOR_add_image means that every load of the image triggers the passed dyLD_callback callback function
Disadvantages isdyld_callback
It’s going to be called back multiple times
When dyLD_Callback is triggered, the BHReadConfiguration method is called internally
- Read previously written class name character data from the Mach-O section (combined into string arrays)
- Iterates through an array of class name strings, passing
BHModuleManager
Register the class,[[BHModuleManager sharedManager] registerDynamicModule:cls];
2.Load registration
By using the macro BH_EXPORT_MODULE(isAsync) to register the +load method when the class is loaded into memory, The actual internal calls the same method [[BHModuleManager sharedManager] registerDynamicModule: CLS];
3. Read the local Plist file
When a BeeHive instance first sets its BHContext property context, it triggers a local PLIST read, loads the module dictionary information for a module, stores it in the BHModuleManager BHModuleInfos array, and registers the module
From the demo, the trigger time is in the delegate
Registration of Services (It’s the same thing as an Annotation)
1.Annotation method registration
1. Compile macros
2. Use mode
3. Results after compilation
4. Fetch content from the __DATA Module by BeehiveServices. The logic is exactly the same as the Module annotation method, except that Module stores an array of strings in memory and Service stores an array of dictionaries
5. Read one by one and register
2.Load registration
In the current framework library, service is registered without load mode. As for why it is not provided, I guess it is to highlight the difference between Module and Service concepts
[the Module]
1. Module is mainly used to deal with the basic operation units of the system and APP declaration cycle level. It is more similar to the abstract management class of the tripartite library developed at the same time, and the abstract management class of APP declaration cycle, as well as some other management classes at the lower level and the iteration cycle is basically unchanged
Module is a system level management class, which is defined by the protocol BHModuleProtocol (which is an APPdelegate proxy forwarding interface)
3. Since Module is a low-level management class, it is reasonable for it to have a load registration method. After all, the low-level management class will need to do some other operations when the class is loaded (this is from the analysis of the usage direction, such as Swizzle and other behaviors).
[Service]
1. From the content provided during registration, not only the corresponding class should be provided, but also the interface protocol of the scope used by it, which is obviously a Module version of the business layer. Due to the characteristics of fast iteration of business, it is reasonable to maintain the associated protocol by users themselves
2. Since Service is a business operation management class, the need for a load is obviously the same as for a Module class, so it makes sense not to provide a load registration method
3, have not thought of 😆, of course, it is also possible to implement, imitate Module load way
3. Read the local Plist file
As with Module, it is triggered when the context property is first set to the BeeHive singleton
Service (class) is registered with Protocol (interface), and module (module) only needs to know the class name. This is also the difference between the two types of components
Module Registration process analysis
1.Annotation method registration
- Retrieves all from the memory segment
module
Name after the string data group, iterate through the number group, and then pass through one by one[[BHModuleManager sharedManager] registerDynamicModule:cls];
registered
[BHModuleManager sharedManager]
Used in singleton form
registerDynamicModule:
Registration entry logic
- (void)addModuleFromObject:(id)object shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent
Enter the actual registration process
Parameter air defense and parameter transfer layer
Module must comply with the corresponding protocol
Ensure compliance, enter the Module class information tag and initialization process
Added:
Point 1:
- The module(BHModule class) hierarchy of system event handling is currently normal and BASIC, with no response methods
basicModuleLevel
Is normal(value: 1), otherwise is basic (value: 1). However, it is not clear from the demo that the division of these two levels has an impact on the response order of system events, and the response order of module at the title level will be further followedPoint 2:
self.BHModuleInfos
Is an array of module information dictionaries, containing the string of class names for each module, the value of the hierarchy, whether it has been initialized, and so on. As for whether or not the flag is initialized, it will be marked as initialized state * from the annotation methodthisThere is a sort, just put into the PList mode registration triggered only], andself.BHModuleInfos
There is no further orderingself.BHModules
Different.self.BHModuleInfos
It is a store list of information, and it does not add the same module repeatedly, because there is already the uniqueness logic to handle modulesPoint 3:
- Add the initialized module instance to
self.BHModules
In the list
Sort the list of self.bhModules instances hierarchically
1, Press basicModuleLeve (from small to large, Basic (0) < Normal (1))
2, If the front is equal, pass modulePriority row (from large to small)
Assumptions: ModuleA (Basic), moduleB(Normal), moduleC(Basic), moduleD(modulePriority=1), moduleE(modulePriority=3), For moduleF(modulePriority=2), the sequence is moduleA, moduleC, moduleB, moduleE, moduleF, and moduleDCopy the code
Register module instances in the corresponding event handling list set. That is, the newly initialized Module instances are grouped according to the corresponding system event so that if a system event is triggered, the message can be quickly and directly forwarded to all module instances that respond to the event
This logic is explained in detail in subpoint 5 below
Trigger the Setup event, init event, and splash event. Of course, the way annotations are registered doesn't implement these methods by default
- Go into more detail about
[self registerEventsByModuleInstance:moduleInstance];
Processing logic
Self.bhselectorbyevent Dictionary table that identifies a system event
Categorize modules and sort them
- Elaborate on the previous three types of events
Setup event, init event, splash event
Trigger logic of
- InitEvent
- Other events such as the setup and Splash events
- TearDownEvent
- Actively remove the Module method
-unRegisterDynamicModule:
2.Load registration
Use the corresponding macro
Result of macro expansion
This macro overrides the class’s + (void)load method and implements an interface method – (BOOL)async for BHModuleProtocol
RegisterDynamicModule = registerDynamicModule; The actual internal call the BHModuleManager method is [[BHModuleManager sharedManager] registerDynamicModule: moduleClass];
The subsequent flow is the same as the Annotation registration module.
The asynchronous method that responds, as described in the final process of the Annotation module registration process, is that after registering and managing the module, three events of the module setup/init/splash will be triggered once. The init event is determined by whether the Module instance responds to the return value of the -async method
3. Read the local Plist file
Trigger timing and location
The bundle’s Plist content
Step through the internal processing logic [[BHModuleManager sharedManager] loadLocalModules];
1. Locate the path of PLIST and handle air defense
2. Get the plist content by path, i.e., an array
Create a temporary empty dictionary (for filtering purposes in Step 5)
4. Add the existing module information dictionary array to the temporary dictionary
The original self.bhModuleinfos contains the following contents, which are module information registered via annotation or load"moduleLevel" : 0,
@"moduleClass" : @"ModuleA"The @"moduleHasInstantiated" : YES,
},
@{
@"moduleLevel" : 1,
@"moduleClass" : @"ModuleB"The @"moduleHasInstantiated" : YES,
},
]
Copy the code
After being added to the temporary dictionary, the dictionary contains the following contents
@{
@"ModuleA" : 1,
@"ModuleB"1.}Copy the code
5. Filter the contents of existing modules, since modules with the same name that handle system events are not allowed to exist, add the information dictionary object of the corresponding PList Module to self.bhModuleinfos. 【 note 】
-
ModuleHasInstantiated is the default value of YES in the module information dictionary for annotation and load registered modules. The module retrieved from the plist is not created
-
Module information in plIST also has a unique key value, modulePriority. Previously, the ordering of modules registered by annotation and load is based on the return value of the module’s response method. Here, the ordering weight is directly attached
So far we know that self.bhModuleinfos may contain both annotation and load module registrations, as well as uninitialized Module information obtained from plist
Continue with [[BHModuleManager sharedManager] registedAllModules]; logic
1, Self. BHModuleInfos sorts by the two keys in the Module dictionary
-
ModuleLevel, modulePriority. This is essentially the level layer (basic or Normal) and priority(numeric size) sorting mentioned earlier in the annotation method registration process.
-
The important thing to note here is that the Module priority key is not listed in the Module dictionary of annotations and loads (there is an ordering of the value, but not much of it is retrieved by the response method).
-
[From this point of view, I think annotation and load are generally level Module classes, while PList is priority class. This is my guess, but the framework has not been updated for many years. If you want to continue to look at it, you have to rely on 😆]
2. Initialize the uninitialized Module, that is, the Module registered from the PList mode, and add it to the temporary array
Merge the newly initialized array of Module instances into the self.bhModules array
4. Finally, we repeat that Module instances are grouped by event category. This logic is explained in the annotation registration analysis above
So far it is clearBHModuleManager
What are the contents of
Service registration process analysis
1.Annotation method registration
1, after reading the array of class-protocol dictionaries from the memory segment, that is, traversing, and then registering one by one
2. Class-protocol Information storage management is registration
3, when storing information through the way of lock to prevent multithreading write problems
-
There are no thread issues in the Annotation of the Module. Why do we need them here?
-
The answer is: because the two conceptually responsible for the scope of the hierarchy caused by the difference, Module is low-level, basically, when the program starts, the opportunity to do a good job according to various dependencies, and the division of labor is clear. A Service, on the other hand, is responsible for the business layer. It can have multiple instances of a Service at the same time or register a new Service temporarily during execution, so it is not surprising that threading problems occur.
-
Given the threading problem, is the lock approach being used properly? (More on 😆 later)
4. The registration logic is simple, where events are triggered while the business is running
2.Load registration
There is no registration method for load
3. Read the local Plist file
1. Trigger local service registration
Read plIST contents and register (store class-protocol dictionary)
3. Repeat possible minor problems
- There’s no anti-reprocessing here, because annotations and plist have the same class-protocol, and if they’re exactly the same, it doesn’t matter, even if they’re repeated, the dictionary’s read time is O(1), A classX-ProtocolX inconsistency can cause serious problems
4. EnableException Enables the problem
-
BHServiceManager has an enableException switch inside it. As you can see from the above code screenshots, this is the switch for the exception prompt, but from the BHServiceManager, I think there is nothing wrong with it, but looking at the whole framework, Why not in BHModuleManager? After all, the enableException switch interface used only in BHServiceManager is actually put into the BeeHive general management class. From the perspective of use, defined in the overview of the table header, Should represent the exception switch for global use, can be the current framework situation, only local use.
So far it is clearBHServiceManager
What are the contents of
The usage process of Module is introduced
2. It can also be seen from this that although the application agent is decoupled from the application delegate level, after all, the entire AppDelegate life cycle function is rewritten, so the coupling is inevitable at this point
3. The specific triggering event logic is described in Module Annotation registration process analysis, which will not be repeated here
This section describes how to use Service
1, We know that the BHServiceManager does not manage the corresponding service instance, only the management of the key value dictionary serviceclass-protocol, so every time you need to use the corresponding service instance is, You need to get the corresponding class based on the corresponding protocol and create the instance
2. Create process analysis
-
1:
- If no corresponding protocol string is passed in, its string name is passed in from the Protocol class
- Check whether the service is registered. Otherwise, an exception is thrown. That is, a service is registered before it is created
-
2:
-
The method argument shouldCache is used to determine whether to read from the cache. This cache is maintained only in the BHContext shareInstance, which is also a dictionary, key: Protocol string name, value: service instance. If it is read from the cache, the service instance corresponding to the protocol is directly returned if it exists in the cache
-
Why is the global Context singleton used to maintain the service instance table? Not only that, the global context singleton also maintains a list of Module names, but it is not enabled
-
Here’s my guess: Context is universal globally and serves as a bridge of parameters between services, modules and services. It is obvious that the use of service instance tables through context is complex and changeable. Maintaining service instance tables through context is obviously to facilitate subsequent interactions. This is also the starting point for coupling, and it is impossible to predict the future impact of the framework on the business or vice versa.
-
-
The third: logical intention is more obvious, if it is too redundant to use words, look at the picture
-
Services also have the concept of singletons, which can also be cached by context
-
Other problems
- Inconsistent code styles
-serviceImplClass
The method feels redundant. Protocol goes from class to string and from string to class
BHAppDelegate
- Let the project
AppDelegate
Class inherits fromBHAppDelegate
BHAppDelegate
Direct overload allAppDelegate
The method of- In overloaded methods, pass
BHModuleManager
Forwards the proxy method to, listening for the eventBHModule
In the instance
Problem analysis
Application generation decoupling
The operation object is BHModuleManager-BHModule
-
Although the module responding to the application proxy has a controllable order of proxy event invocations, the order is not obvious and needs to be checked on a class-by-class basis
-
The corresponding operation object of the call decoupling between modules is BHServicemanager-objects that comply with BHServiceProtocol
-
Url-ruouter Decoupled the corresponding operation object is bhRouter-BHServicemanager-An object that complies with BHServiceProtocol
The direction of
Can be used for reference
A tripartite library triggered by startup in AppDelegate, encapsulated by module
- Increased debug difficulty because the original intuitive code logic is scattered
- This is not easy to implement if there is a confidently coupled interaction between three libraries. BeeHive modules interact independently of each other
reference
- BeeHive author online video and PDF