Series of articles:OC Basic principle series.OC Basic knowledge series.Swift bottom exploration series.IOS Advanced advanced series
In the previous article we saw how to create componentized projects. In this article we will talk about the focus of componentization: componentized communication
Componentized communication method
At present, there are three mainstream ways to understand:
- 1.
URL
routing - 2.
target-action
- 3.
protocol
matching
Protocol trial programming
Use protocol definition specifications at the compile level to implement in different places to achieve distributed management and maintenance of components. This approach also follows the principle of dependency inversion and is a good object-oriented programming practice.
But the plan is also obvious:
- Due to the
Protocol programming lacks a unified scheduling layer
, resulting inDifficult to centralize
, especiallyProjects get bigger
,The team has more
In the case of,Architectural governance becomes more and more important
. Protocol programming interface
Define patternToo specification
, thus making the architectureNot flexible enough
. When you need to introduce aNew design patterns to develop
We will find outIt's hard to fit into the current architecture
,Lack of architectural uniformity
.
middle-borns
It uses the intermediary unified management to control the call relationship between components in the entire life cycle of the App. At the same time, iOS also needs to maintain consistency in the design of component interfaces, so as to facilitate the unified call of intermediaries.
The split components depend on the intermediary, but there is no interdependence between the groups. Communication between components is easier to manage because all other components depend on this intermediary, and communication between components is uniformly scheduled through the intermediary. New design patterns can also be easily added on top of the intermediate, making the architecture easier to extend.
A good architecture is robust and flexible. Easy management of an intermediate architecture leads to a more stable architecture, and easy scalability leads to flexibility.
URL routing
This is also the communication scheme used by many iOS projects. It is based on route matching, or according to the naming convention, the Runtime method is used for dynamic invocation. The IDEA of URL routing adopts the middle-of-the-road mode
- Advantages:
Implement a simple
- Cons: Need
Maintain the string table
Or,Depends on the naming convention
.Can't
inCompile-time exposure
Out of allThe problem
That need to be inThe runtime
In order toFind the error
.
Advantages and disadvantages of URL routing
“Advantages”
Extremely dynamic
, suitable for apps with frequent operation activities. For example, e-commerceConvenient unified management
Multi-platform routing rulesEasy to adapt to URL schemes
“Defect”
The parameter transmission mode is limited
And,Cannot use compile time for parameter type checking
(All arguments are converted by string)This applies only to interface modules
.Does not apply to generic modules
The parameter format is not clear
Is a flexible dictionary, and you need a place to look at the parameter formatDo not support the storyboard
Depends on string hardcoding
, difficult to manage, mogujie specially to do a background management of this partThere is no guarantee that all modules used will exist
The decoupling capability is limited
URL “register”, “implement”, “use” must be usedSame string rules
, changes made by either party will invalidate the code elsewhere, andRefactoring is difficult
The URL routing mode is mainly MGJRouter represented by Mogujie
MGJRouter
The realization idea is as follows:
The App is started
Instantiate the component modules, and then theseThe component registers the URL with MGJRouter
And, in some cases,No instantiation is required
.Using Class registration
- when
Component A
Need to becall
componentB when
.Pass the URL to the ModuleManager
.Parameters are passed as GET along with the URL
.Similar openURL
. And then byThe ModuleManager is responsible for scheduling component B
And finally finish the task.
MGJRouter usingURL routing
But hisDecoupling ability
orCo., LTD.
In addition to the MGJRouter above, there are the following tripartite frameworks
- routable-ios
- JLRoutes
- HHRouter
target-action
This solution is based on the OC runtime/category feature to dynamically fetch modules, such as NSClassFromString to fetch classes and create instances, and NSInvocation to dynamically invoke methods via performSelector+NSInvocation
This method is mainly represented by CTMediator of Casatwy
CTMediator
The implementation of CTMediator:
- 1.
Use classification
forAdd a new interface to the route
In theinterface
throughString gets the corresponding class
- 2. By
runtime
Create an instance,Dynamically invoke the instance's methods
Specific usage: The reference relationships between modules are as follows: “Advantages”:
- using
classification
canThe statement interface
,Compile the inspection
- implementation
lightweight
【 Disadvantages 】:
- Need to be in
Mediator and target
In theTo add
eachinterface
.modular
whencode
The moretedious
- in
category
Still want toHard coding of strings is introduced
For internal useDictionary and reference
And, to some extent, there are also alpha and betaURL routing same problem
There is no guarantee that the module being used exists
.Target after modification
, the user can only use theErrors can only be found at runtime
- create
Too many Target classes
, resulting inTarget class flood
CTMediator source code analysis
CTMediator uses URL routing
This method is mainly aimed at mutual tuning of remote apps. The jump between apps is realized through openURL, and data is transferred through URL
CTMediator usesRuntime decoupling
, the decoupling core method is shown as follows:
performTarget:action:params:shouldCacheTarget:
The method is mainly righttargetName
andactionName
Fault-tolerant processing, that is, processing that does not respond to the calling method.- This method encapsulates
safePerformAction:target:params
Method, enter the parameterTargetName is the object that calls the interface
.ActionName is the name of the invoked method
.Params are parameters
. - And you can also see in the code that there are only
The object of the class that satisfies the Target_ prefix
andOnly the Action_ method can be used by CTMediator
. At this point, we can see the advantage of the intermediary architecture, which is conducive to unified management and can easily control the rules formulated.
So let’s focus on thatWith the action
In the case
protocol class
Protocol matching is implemented as follows:
- 1. The
protocol
And the correspondingclass
forDictionary matching
- 2. By using
Protocol for the class
Again,Dynamically creating an instance
A typical tripartite framework for Protocol is Alibaba’s BeeHive. BeeHive borrowed from the Spring Service, Apache DSO architecture concept, using AOP+ extension App life cycle API form, the business function, basic function module way to solve complex problems in large applications, and between modules in the form of Service call, complex problems will be divided, Modularize services in an AOP manner
BeeHive core ideas
- 1.
Call between modules
From directCall the corresponding module
To becomeThe form of calling a Service
.Avoid direct dependencies
- 2. Distribution of App life cycle will
The coupling is split logically in the AppDelegate
, each module starts withMicro application
The form of exists independently.
“Advantages”
- 1.
Using an interface call
To realize parameter transferType safety
- 2. Use it directly
The protocol interface of the module
.No need for repeated encapsulation
“Defect”
- 1. Use
Framework to create all objects
, which does not support external parameter transmission - 2. Use
OC
theruntime
Create an object,Do not support the Swift,
- 3. Only
protocol
andclass
The match,More complex creation methods and dependency injection are not supported
- 4.
There is no guarantee that the protocol used must have a corresponding module
Also,You can't tell directly
aWhether protocol can be used to get modules
In addition to BeeHive, there is Swinject
Module registration in BeeHive
BeeHive manages each module mainly through the BHModuleManager. Only registered modules are managed in the BHModuleManager
BeeHive provides three different invocation forms, static plist, dynamic registration, and annotation. Module and Service are not associated. Each Service Module can independently implement functions of Module or Service.
Annotation registration
This way is mainly throughBeeHiveMod macro
forThe Annotation tag
Here for__attribute
A few points need to be made
- The first parameter
used
:Modify functions
.Is decorated, informs
In the future, even thoughThe function is not referenced
In theRelease
Also underIt's not optimized
. ifWithout this embellishment
, thenRelease environment linker
underRemove sections that are not referenced
. - Through the use of
__attribute__((section("name")))
toSpecify which paragraph
.The data is tagged with __attribute__((used))
.Linker prevention
willOptimize the deletion of unused segments
And thenInject modules
toIn the __DATA
Now that the Module has been stored in a special section of the Mach-O file, how do I get it?
The BHReadConfiguration method is displayed
, mainlyThrough the Mach - O
findstorage
theData segment
.Take out put in array
In the
Read the local Pilst file
- First of all, you need to
Set up a path
- create
plist
File,plist
The file format is also an array containing multiple dictionaries. There are two keys in the dictionary, one is"moduleLevel"
And the other one is"moduleClass"
. Pay attention toThe root
The name of the array is"moduleClasses"
.
- Go to the loadLocalModules method and basically get the array from the PList and add the array to the BHModuleInfos array
Load method registration
The methodTo register the Module
Is in theThe load method
insideRegister Module classes
- Enter the
registerDynamicModule
implementation
The bottom layer is the same as the first one, and it will eventually go toaddModuleFromObject:shouldTriggerInitEvent:
In the method
- The load method can also be used
BH_EXPORT_MODULE macro
Instead of
The BH_EXPORT_MODULE macro can pass in a parameter that indicates whether the Module is loaded asynchronously, if YES, or synchronously if NO.
BeeHive module event
BeeHive provides life cycle events for each module to interact with the BeeHive host environment and sense changes in the module’s life cycle.
BeeHive
Each module receives events. inBHModuleManager
All of themThe event
beDefined as
theBHModuleEventType enumeration
. As you can see below, there are two special events, one isBHMInitEvent
, one isBHMTearDownEvent
There are three main types
- 1.
System events
: Mainly refers toApplication life cycle events
The general practice isAppDelegate
Instead ofInherited from BHAppDelegate
- 2.
Application events
: Official flow chart, whichModSetup, modInit
Can be used forEncoding the implementation of each plug-in module
theSetup and initialization
.
- 3. Customize events
All of the above events can be calledBHModuleManager的triggerEvent:
To deal with.As can be seen from the above code, removeBHMInitEvent Indicates the initialization event
andBHMTearDownEvent Event that a Module is removed
All events except these two special events are calledhandleModuleEvent:
Method, whose internal implementation is mainlyIterate through the BHModules instance array
, the callperformSelector:withObject:
Method implements the corresponding method call
Note: all modules here must be BHModuleProtocol compliant or they will not receive messages for these events.
The BeeHive module is called
In BeeHive, the BHServiceManager manages each Protocol. BHServiceManager manages only registered protocols.
There are three ways to register the Protocol, which correspond to the Module registration mentioned above
Annotation registration
Read the local PList file
- First, as with Module, you need to set the path first
- Set the plist file
- Also register services in setContext
Protocol registration
It’s basically callingBeeHive
The inside of thecreateService:
completeprotocol
The registration of createService
Will firstCheck whether the Protocol is registered
Before. Then fetch the corresponding Class in the dictionary, if implementedshareInstance
Method, thenCreate a singleton object
If not, create oneInstance objects
. If you stillImplementing the singleton
, can be furtherimplInstance
andserviceStr
Lambda plus lambdaBHContext
theservicesByName
The dictionaryThe cache
To get up. This can then be passed with context
- Enter the
serviceImplClass
Implementation, as you can see hereprotocol
And the class is passedThe dictionary
The binding,protocol
As akey
.ServiceImp (the name of the class) as value
Module & Protocol
Here’s a summary:
- for
Module
:An array of storage
- for
Protocol
Through:The dictionary
willprotocol
withclass
forThe binding
.key
forprotocol
.value
forserviceImp
The name of the class
Auxiliary class
BHConfig
Class: YesThe singleton
And there’s one insideNSMutableDictionary
The type ofThe config attribute
This property maintains a number of dynamic environment variables asBHContext
Supplementary existence ofBHContext
Class: is a singleton with two internal instancesNSMutableDictionary
Properties of, respectivelymodulesByName
andservicesByName
. This class is mainly used to store context information. Such as:application:didFinishLaunchingWithOptions:
A large amount of context information can be initialized
BHTimeProfiler
Class: used to performComputational time performance
In terms ofProfiler
BHWatchDog
Class: used to start a thread,Listen for the main thread to block
Related articles recommended links
- BeeHive – an elegant but still improving decoupling framework
- BeeHive, an iOS module decouple practice
- Good mobile iOS componentized (modular) architecture design practice
The last
Several months later, I stayed up late to write a lot of articles about the bottom of OC. In this process, I got a new understanding of OC. The content research of OC is now in the end! Later, I will share some ideas of encapsulation and architecture that I think are better in the actual development. I will continue to explore the bottom of Swift, and will continue to update the article. I hope we can communicate with each other and make progress together. Thank you very much!