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)
    • BHModuleNeed to followBHModuleProtocolThe system-level event interface is defined inBHModuleProtocol)
    • By itselfalloc initMode self-initializes modules when not presentBHModuleManagerUnder the management
  • BHContext:

    • Manages the application environment, global configuration, and event-triggering context objects (that is, context parameters for event methods)
    • Also includesBHShortcutItem,BHOpenURLItem,BHNotificationsItem,BHUserActivityItem,BHWatchItem
  • BHModuleManager: Responsible for [registering] the BHModule and managing it

    • BHModulesArray:
      • Stores registered BHModule instances (of each class)BHModuleThere can only be one instance, and filtering is done during registration,isKindOfClassIs no longer counted in this instance.)
      • The sorting
        • According to the firstbasicModuleLeve(From small to large, basic < normal)
        • If the front is equal, passmodulePriorityRow (from large to small)
    • BHModuleInfosDictionary array: dictionary of module information created when registering a module, including module class, module level, whether module has been Instantiated, etc
    • BHSelectorByEventEvent table dictionary: Manages default system event types and custom event types
    • BHModulesByEventEvent-response instance array table dictionary: module instances that will be able to respond to events of the same typeBHModulesThe 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
  1. By using the macro Beehive emod (The name of the classWrites 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

  1. 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_callbackIt’s going to be called back multiple times

When dyLD_Callback is triggered, the BHReadConfiguration method is called internally

  1. Read previously written class name character data from the Mach-O section (combined into string arrays)

  1. Iterates through an array of class name strings, passingBHModuleManagerRegister 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

  1. Retrieves all from the memory segmentmoduleName after the string data group, iterate through the number group, and then pass through one by one[[BHModuleManager sharedManager] registerDynamicModule:cls];registered

  1. [BHModuleManager sharedManager]Used in singleton form

  1. registerDynamicModule:Registration entry logic

  1. - (void)addModuleFromObject:(id)object shouldTriggerInitEvent:(BOOL)shouldTriggerInitEventEnter 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 methodsbasicModuleLevelIs 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 followed
  • Point 2:

    • self.BHModuleInfosIs 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 methodthisself.BHModuleInfosThere is no further orderingThere is a sort, just put into the PList mode registration triggered only], andself.BHModulesDifferent.self.BHModuleInfosIt is a store list of information, and it does not add the same module repeatedly, because there is already the uniqueness logic to handle modules
  • Point 3:

    • Add the initialized module instance toself.BHModulesIn 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

  1. Go into more detail about[self registerEventsByModuleInstance:moduleInstance];Processing logic

Self.bhselectorbyevent Dictionary table that identifies a system event

Categorize modules and sort them

  1. Elaborate on the previous three types of eventsSetup event, init event, splash eventTrigger logic of
  • InitEvent

  • Other events such as the setup and Splash events

  1. TearDownEvent

  1. 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 clearBHModuleManagerWhat 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 clearBHServiceManagerWhat 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
    • -serviceImplClassThe method feels redundant. Protocol goes from class to string and from string to class

BHAppDelegate

  • Let the projectAppDelegateClass inherits fromBHAppDelegate
  • BHAppDelegateDirect overload allAppDelegateThe method of
  • In overloaded methods, passBHModuleManagerForwards the proxy method to, listening for the eventBHModuleIn 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