preface

Today, while reviewing the chapter on symbol tables in “Self-cultivation of Programmers”, I suddenly had a brainwave and thought of an application scenario of this knowledge, so I have this article.

If you are not familiar with iOS componentization, you can read another article by me: Understanding the nature of iOS componentization and routing. This article mainly discusses how to use weak symbols to decouple iOS components.

Brief description of strong and weak symbols

Functions and global variables need to be compiled with unique symbolic names so they don’t get confused when linking. Variable names in code written by programmers are modified to be symbolic names, such as fun in C being modified to _fun. For C/C++, the compiler default functions and initialized global variables are strong symbols, and uninitialized global variables are weak symbols. You can define a weak symbol using __attribute__ ((weak)). The compiler has the following rules for symbols:

  • Strong symbols are not allowed to be defined more than once.
  • Select a strong symbol when multiple symbol names are the same and only one strong symbol is present.
  • If multiple symbol names are the same and all weak symbols are weak, select the symbol that occupies the largest space.

That is, symbol names can be repeated as long as there is at most one strong symbol.

The core idea

At this point many of you probably know what I’m trying to do. The problem of iOS componentation core is decoupling. It needs to call the target business in some ways without importing the target business module, so it can be easily solved by defining a strong symbol in the target business module and a weak symbol in the user side.

Specific implementation

Although the idea is clear, but everyone is very lazy, always try to reduce some implementation cost. Author simple design, very easy to understand, see the code DEMO.

For example, for the EAim business module, create a file EAimRouter. M to place the relevant routing methods:

void ERouterGotoEAimController(NSString *name, void(^callBack)(void)) {
    EAimController *vc = [EAimController new];
    vc.name = name;
    vc.callBack = callBack;
    [UIViewController.yb_top.navigationController pushViewController:vc animated:YES];
}
Copy the code

These routing C language methods and the EAim business module are a REPO, and they are the strong notation needed for decoupling. In order for weak symbol related code to be maintained by the EAim business, it is necessary to create an additional REPO, called eaimMediator.h, which is just a header file:

__attribute__ ((weak)) void ERouterGotoEAimController(NSString *name, void(^callBack)(void)) {
    ERouterNotFound(name, callBack, nil);
}
Copy the code

Eaimmediator.h repo is imported by the caller and is clearly decouplefrom the business. These C methods are the weak symbols needed for decoupling. If the current business module import the EAim module, this method will be of the same name defined in EAim ERouterGotoEAimController coverage; If the EAim module is not imported, ERouterNotFound is used for fault tolerance.

ERouterNotFound is stored in emediator.h /.m to process routes that are not found:

void ERouterNotFound(id params, ...) {
    if (params) {
        va_list argList;
        va_start(argList, params);
        id arg = params;
        do {
            NSLog(@"cur arg: %@", arg);
        } while ((arg = va_arg(argList, id)));
        va_end(argList);
    }
    NSLog(@"router not found");
}
Copy the code

Emediator. h/. M is a useful tool for storing routing-related processing algorithm logic, which is imported by all required service modules as a common service component.

Advantages and disadvantages analysis

There are generally three ways to achieve component decoupling: Protocol, Block, and Runtime dynamic invocation. The advantages and disadvantages of these methods can be explained in detail by referring to the nature of iOS componentalization and routing.

The weak symbol method is the same as the Protocol method, there is no unified interception, so to implement the interception of all routes will need to manually call the interception method, which is a disadvantage compared to the Block and Runtime methods.

The advantage of weak notation over Block and Runtime is that it doesn’t require a lot of hard coding, why a lot? Because the compiler does not check that the weak symbol is exactly the same as the strong symbol, this can be hard coded, but can be easily handled by copy and paste, and because the weak symbol maintainer is also the corresponding business team, so the cost is relatively low.

Weak notation has an advantage over Protocol and Block in that no registration is required, and the compiler automatically statically determines the address of the symbol reference according to the rules mentioned earlier.

Another advantage of the weak notation approach is that the code is very concise, with almost only strong symbols and a corresponding weak symbol, and there is not a lot of middle layer processing code. One caveat, however, is that some complex transformations may be required when a project includes Swift, and perhaps a pure Swift project can be designed with the features of the Swift language itself.

After the language

So far, this seems to be a viable solution for iOS component decoupling. However, the whole process is just a small inspiration of the author today, the implementation of the scheme may have some hidden dangers, the scene has not been completely practiced, so welcome the big guy criticism 😁.