preface
Hide the details and expose the abstractions.
As ambitious engineers, we want code to evolve over version iterations, not deteriorate; You’ll also learn more skills and tools to better design, implement, and organize code. I came across the sharing of an Apple engineer, so I added some of my own experience and feelings to make some conclusions.
The body of the
1. Code organization
1. Use group
As an iOS engineer, Xcode should be one of the most familiar tools. In older versions of Xcode, when creating a new directory, it only creates a reference, not the same directory at the same time. In the new version of Xcode, directories are created in the form of group, and directories will be created in the same path.
For example, when creating a New Group, create a New Group directory under the Audio directory as well. If the project code was created a long time ago in Xcode, it is a good idea to check the directory so thatXcode’s project file directory has the same structure as the actual file directory; If the project is created with new Xcode, try to create groups in Xcode.
2. Split large files
If your project uses storyboards, you can split larger storyboard files into multiple storyboards by reference. This increases the speed of opening and reduces the amount of conflict when multiple people are working together. But none of the projects I’ve worked on have used storyboards. The problem with large files is more with.m files. Take one file in our project for example:
This 2,000-line. M file is not a one-time thing, but a slowly growing document with a dozen iterations of logic. This is what we call historical technical debt. Technical debt can occur for a variety of reasons, from poor framework design in the beginning to implementation irregularities, to code bloat caused by collaborative development. When problems are identified, the technical debt needs to be paid off.
The.m file splitting first needs to sort out the core logic of the business, abstract out the status information and key parameters of the module,Change the logic added by the external business in.m to depend on the state provided by.m, and the status can be thrown out by notification, message, etc.
Core but cohesive logic can be encapsulated using xxLogic, and.m files rely directly on that xxLogic. Or you can aggregate it into a Category in your.m file;
After this process, the.m file can be greatly reduced, and after sorting out the internal and external dependencies, the subsequent new logic does not need to look at the.m file, but rely on the following. H file.
3. Pay attention to Xcode tips
Keep Xcode project Settings up to date and use Xcode’s built-in performance optimizations. When Xcode displays the box below, apple engineers recommend clicking the Perform Changes button if there are no specific requests.
During compilation, a warning given by Xcode may be a Bug when running online. It is recommended to enable the Treat Warnings as Errors option during debug development. Trace back to the root cause of the problem and resolve each compile warning.
If the problem is known and no solution is available, you can ignore it using xcode instructions to avoid blocking the compilation run. (The specific type of warning can be viewed in Xcode’s Issue Navigator, and the shortcut is Command +5)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// ingore code
#pragma clang diagnostic pop
Copy the code
Get rid of useless code
We sometimes submit a section of code that is commented out, probably because the code is not needed now but may be used in the next version, so it will be commented out temporarily so it doesn’t affect the operation. But imagine if everyone on the team had this habit, would there be a lot of useless code in the project? And the code may never be useful. So don’t be afraid to delete unnecessary code, and even if you need it, you can use your version control tool to find historical code.
Second, code management
Version control system has become one of the necessary tools for development. SVN used to be an efficient tool for version management, as well as for TortoiseSVN in Windows. However, with the advent of Git, SVN has been gradually phased out.
1. Submission independence
A complex feature often consists of multiple requirement points, and the development process can take days. It is possible to split the submission of requirements into multiple submissions to make each submission as independent as possible, and Xcode can see the submission comment information for each line of code. Git commit information to see the reason for this code. Click the corresponding information on the right and select Show Commit. You can also see the details of the corresponding commit.
One can remember why a piece of code was written yesterday, but it’s hard to remember why a piece of code was written months or even years ago.
2. Branch management
In order to maintain the convenience of the development phase, alpha branch is provided as the integrated branch of daily development; In order to ensure that the extranet code can be checked, beta branch is provided as a packaged branch for version release. When a version is released, you also need to tag the corresponding version, such as release_1.0.0.10. Daily requirements development (feature branch) and bug repair (bug branch) are all developed in the non-trunk branch, and finally integrated into the alpha branch. Integration requirements According to the actual situation of the team, it can be completed before integration of branches, or unified acceptance after integration.
3, Code Review
Code Review (CR) is what happens when a branch joins in and is an essential part of a mature development team. CR helps teams unify their code styles, including function naming, variable naming, code organization style, and so on. At the same time, CR requires the code to be readable and to include too many changes in a single commit.
Three, documentation,
1. Necessary notes
Good code is self-explanatory and can clearly describe logic without needing comments to assist description. A special piece of logic, however, needs a comment to describe why it exists, so that it can return to the point of influence after the change. For example, a classic logic of dispatch_after for 1 second may be used to avoid some abnormal cases, or it may be required by the product side.
2. Description of external methods
In the usual development process, in addition to paying attention to the variable and method name to have meaning, the annotation of the external method can clearly describe the required parameters. For example, the following method:
Select the corresponding method in Xcode and press option+? You can see the description of the method and the requirements for each parameter. If the method does not already have a description, press Option + Command +? Automatically generate a description to be added.
3. Document accumulation
As the business evolves, the code in the project inevitably expands rapidly, making it difficult to read the code directly. Documentation is needed to help you understand the status of each module. Documentation should avoid verbose logic details, but rather describe how the module works in terms of overall design and considerations. At the same time, the design process should also be based on the previous technical scheme design.
Cultivate the habit of writing documents for the team, organize the review of technical solutions at the early stage of each version, and prepare a design document of technical solutions by the engineer responsible for complex requirements, which can achieve twice the result with half the effort.
4. Convenient tools
When people mention Xcode’s analysis tools, the first thing they think of is an Instrucment toolset. But there are some handy tools for actual development.
(1) Network Link
After mobile phone connection, you can press Command + Shift +2 in Xcode and select the corresponding device to get into a specific Network environment. After mobile phone connection, you can set up developer/Network Link.
2, the Address Sanitizer
Address Sanitizer is a memory error detection tool that adds tags through malloc/ Free. For example, in the following code, the buF pointer creates 1024 memory, manually frees, and then accesses the elements of the buF pointer. This code compiles normally, but does not necessarily crash at run time. It may evolve into an accidental bug that is difficult to locate. When using the Address Sanitizer tool, an error occurs at line 130: Use of dealloccated memory.
To open it, go to the Scheme option and select Address Sanitizer.
3, Thread Sanitizer
Thread Sanitizer is a Thread error detection tool that detects errors associated with multithreaded data access, as shown in the following code. STestNum is a static global variable, and multiple threads are created to manipulate it, triggering a Data Race.
To open it, select Thread Sanitizer in the Scheme option.
Thread Sanitizer focuses on multi-threaded access to data, which is implemented by recording memory access. It cannot locate multi-threaded crash problems, such as the following crash:
4. Main Thread Checker
Main Thread Checker is a UI Checker for multithreaded operations. UI operations can only be performed on the Main Thread and will trigger a warning if performed on sublines.
To enable this function, select Main Thread Checker from the Scheme option.
5, the Debug Gauges
When running the debug program, the DEBUG Gauge can quickly view information about the CPU, Memory, Disk, and Network.
I’m going to open it up with Xcode by saying Command +7.
5. Development Suggestions
1. The principle of minimum dependence
The operation of a piece of logic often requires external variable input. Sometimes, for the sake of easy development, functions are called without passing parameters. Instead, they are called with global variables, self Pointers, and so on to get the required data directly. But this can lead to code logic chaos. When coding, it is highly recommended to use the principle of minimum dependencies: use as few external dependencies as possible. In the case of functions, a method that xx logically handles should rely only on function arguments. So the input and output of the function are fixed, even if the function is placed somewhere else, as long as the input of the function is constant, then the output of the logic is constant. Similarly, in addition to functions, there are views, models, etc., and as little as possible to rely on external data, external modules, the logic is more independent, easier to achieve directly reusable view, model, etc.
2. Componentization & modularization
When implementing functionality, remove coupling as much as possible; The library composed of specific functions is the component, write the new function code as far as possible to realize the component direction; Modularization refers to the aggregation of codes according to functions and businesses according to business forms, which is equivalent to a library combining various components and business logic. An important feature of modularization and componentization is Pod, which separates these specific and independent functional codes and business codes from the main project, abstracts the interfaces required by the business, and then re-introduces them into the main project through Pod dependencies. In this process, not only does the code move to the Pod library, but some business decoupling and dependency abstraction is also required. The benefits are also obvious: in development, each business is relatively independent after modularization, so it can focus more on its own business logic, even if the impact of business errors is relatively controllable; In terms of efficiency, after modularization, binary components can be made to speed up compilation; In terms of management, the component owner has a stronger awareness, which facilitates the addition of data monitoring. Architecturally, it forces interface oriented programming to avoid a lot of coupled glue code.
conclusion
Part of this paper refers to WWDC2019, combined with some work experience, to do more suitable for their own exposition. I also sorted out the technical optimization direction for the next period of time: daily business iteration, to ensure the uniform style of new code through CR; Complex business needs, need to do technical solution review, pool wisdom; Existing historical debts, small modules and micro integration are realized, and large businesses are restructured in a special way. Attention should be paid to human input, business impact and income assessment.