iOS development specifications

Preface

This article is compiled and written with reference to some of my own code specifications and the code specifications of many other partners on the Internet.

Purpose

In order to facilitate project maintenance and standardized development, promote the efficiency of Code Review among members, for beautiful code, and for the harmony of members, the following development specifications are proposed. If you have better suggestions, please feel free to submit them.

The intended audience for this document includes: iOS developers.

It’s begun, it’s very long

This specification is divided into three parts:

1. Core principles: This article introduces the core principles followed by this code specification.

2. Universal specifications: Universal code specifications that are not limited to iOS (using C language and Swift language).

3. iOS specifications: Code specifications only applicable to iOS (using Objective-C language).

1. Core principles

Principle 1: Code should be concise, easy to understand, and logically clear

Because software needs people to maintain it. This person may not be you in the future. So write programs for people first, and computers second: don’t pursue skills too much and reduce the readability of the program. Concise code leaves no room for bugs to hide. Write code that is obviously bug-free, not code that is bug-free.

Principle 2: Program for change, not for requirements.

Needs are temporary, only change is eternal. This iteration cannot be just for current needs. It is responsible to write a program that is highly scalable and easy to modify.

Principle 3: Ensure the correctness of the program first to prevent excessive engineering

Over-engineering: Excessive consideration of extension and reuse issues before correct and usable code is written, making the project overly complex.

1. Solve the immediate problem first, solve it well, and then consider future expansion issues.

2. First write usable code, think about it repeatedly, and then consider whether it needs to be reused.

3. First write code that is usable, simple, and obviously bug-free, and then consider testing.

2. General specifications

About braces

In control statements (if, for, while, switch), the beginning and end of the curly braces

It is recommended to write like this:

white(someCondition) {
    
}

//函数
void function(param1, param2) {
    
}

operator

1. The gap between operators and variables

1.1 There is no space between the unary operator and the variable:

1.2 There must be a space between the binary operator and the variable

!bValue
++iCount

fHeight = fWidth + fLength;
for(int i = 0; i < 10; i++)

variable

1. A variable has one and only one function. Try not to use one variable for multiple purposes.

2. Variables should be initialized before use to prevent uninitialized variables from being referenced.

3. Local variables should be as close as possible to the place where they are used.

推荐这样写:
func someFunction() {
let index = ...;
//Do something With index
...
...
let count = ...;
//Do something With count
}

不推荐这样写:
func someFunction() {
let index = ...;
let count = ...;
//Do something With index
...
...
//Do something With count
}

if statement

1. Don’t use too many branches, and be good at using return to return early.

推荐这样写:
- (void)someMethod {
    if (!goodCondition) {
        return;
    }
    //Do something
}

不推荐这样写:
- (void)someMethod {
    if (goodCondition) {
        //Do something
    }
}

wrong situation

A more typical example I have encountered in JSONModel:

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError)err{
    //⽅法1.参数为nil
    if (!dict) {
        if (err) *err =[JSONModelError errorInputIsNil];
        return nil;
    }
    
    //⽅法2.参数不是nil,但也不是字典if (![dict isKindOfClass:[NSDictionary class]]) {
    if (err) *err =[JSONModelError errorInvalidDataWithMessage:@"Attempt
                    to initialize JSONModel object using initWithDictionary:error: but the
                    dictionary parameter was not an 'NSDictionary'."];
                    return nil;
}

//⽅法3.初始化
self =[self init];
if (!self) {
    //初始化失败
    if (err) *err =[JSONModelError errorModelIsInvalid];
    return nil;
}

//⽅法4.检查⽤户定义的模型⾥的属性集合是否⼤于传⼊的字典⾥的key集合(如果⼤于,则返回NO)
if (![self __doesDictionary:dict
    matchModelWithKeyMapper:self.__keyMapper error:err]) {
    return nil;
}
                    
//⽅法5.核⼼⽅法:字典的key与模型的属性的映射
if (![self __importDictionary:dict withKeyMapper:self.__keyMapper
                   validation:YES error:err]) {
    return nil;
}
                    
//⽅法6.可以重写[self validate:err]⽅法并返回NO,让⽤户⾃定义错误并阻拦model的返回
if (![self validate:err]) {
    return nil;
}
                    
//⽅法7.终于通过了!成功返回model
return self;
}

It can be seen that here, various error situations are first judged and then returned in advance, and the most correct situation is

Return at the end.

2. If the conditional expression is very long, you need to extract it and assign it to a BOOL value

推荐这样写:
let nameContainsSwift = sessionName.hasPrefix("Swift")
let isCurrentYear = sessionDateCompontents.year == 2014
let isSwiftSession = nameContainsSwift && isCurrentYear
if (isSwiftSession) {
// Do something
}
不推荐这样写:
if ( sessionName.hasPrefix("Swift") && (sessionDateCompontents.year ==
2014) ) {
// Do something
}

3. The judgment of the conditional statement should be that the variables are on the left and the constants are on the right.

推荐这样写:
if (count == 6) {
    
}
或者
if (object == nil) {
    
}
或者
if (!object) {
    
}

4. The implementation code of each branch must be surrounded by braces

Switch statement

1. Each branch must be enclosed in curly brackets

//推荐这样写:
switch (integer) {
    case 1: {
        // ...
    }
        break;
    case 2: {
        // ...
        break;
    }

    default:{
        // ...
        break;
    }
}

2. When using enumeration types, there cannot be a default branch. Except when using enumeration types, there must be a default branch.

When using an enumeration type in a Switch statement, if the default branch is used, the compiler will not be able to check the newly added enumeration type in the future.

function

1. The length of a function must be limited to 50 lines

Generally speaking, when reading a function, if you need to span a long vertical distance, it will greatly affect the code reading experience. If you need to roll your eyes or code back and forth to see the entire method, it will greatly affect the coherence of thinking and have a greater impact on the speed of reading code. The best situation is that the entire code of the method can be seen at a glance without rolling your eyes or the code.

2. A function only does one thing (single principle)

The responsibilities of each function should be clearly defined (just like a class).

// 推荐这样写:
dataConfiguration()
viewConfiguration()

// 不推荐这样写:
void dataConfiguration() {
    ...
    viewConfiguration()
}

3. For functions (methods) with return values, each branch must have a return value

// 推荐这样写:
int function() {
    if(condition1){
        return count1
    }else if(condition2){
        return count2
    }else{
        return defaultCount
    }
}

4. Check the correctness and validity of the input parameters, and return immediately if parameter errors occur

// 推荐这样写:
void function(param1,param2) {
    if(param1 is unavailable){
        return;
    }
    if(param2 is unavailable){
        return;
    }
 //Do some right thing
}

5. If there are the same functions inside different functions, the same functions should be extracted and used as another function.

// 原来的调⽤:
void logic() {
    a();
    b();
    if (logic1 condition) {
        c();
    } else {
        d();
    }
}

// 将a,b函数抽取出来作为单独的函数
void basicConfig() {
    a();
    b();
}

void logic1() {
 basicConfig();
 c();
}

void logic2() {
 basicConfig();
 d();
}

6. Extract the more complex logic inside the function as a separate function

The unclear code in a function (more logical judgments and more lines) can often be extracted to form a new function, and then call it in the original place so that you can Use meaningful function names instead of comments to increase program readability.

Give an example of sending an email:

openEmailSite();
login();
writeTitle(title);
writeContent(content);
writeReceiver(receiver);
addAttachment(attachment);
send();

// 中间的部分稍微⻓⼀些,我们可以将它们提取出来:
void writeEmail(title, content,receiver,attachment)
{
    writeTitle(title);
    writeContent(content);
    writeReceiver(receiver);
    addAttachment(attachment);
}

// 然后再看⼀下原来的代码:
openEmailSite();
login();
writeEmail(title, content,receiver,attachment)
send();

7. Avoid using global variables and class members to transfer information, and use local variables and parameters as much as possible.

In a class, it is often necessary to pass certain variables. And if the variable that needs to be passed is a global variable or attribute, some friends don't like to use them as parameters but access them directly inside the method:

class A {
    var x;
    func updateX() {
        ...
        x = ...;
    }
    func printX() {
        updateX();
        print(x);
    }
}

We can see that in the printX method, there is no value transfer between the updateX and print methods. At first glance, we may not know where x comes from, which reduces the readability of the program. And if you use local variables instead of class members to pass information, then these two functions do not need to depend on a certain class, and are easier to understand and less error-prone:

func updateX() -> String{
    x = ...;
    return x;
}
func printX() {
    String x = updateX();
    print(x);
}

Comment

Most of the excellent code is self-describing. We can use the program code itself to express what it is doing without the assistance of comments. But this does not mean that you must not write comments. There are three situations where it is more suitable to write comments:

1. Public interface (comments should tell people who read the code what functions the current class can achieve).

2. Code involving deeper professional knowledge (comments should reflect implementation principles and ideas).

3. Code that is prone to ambiguity (but strictly speaking, code that is prone to ambiguity is not allowed to exist).

In addition to the above three situations, if others can only rely on comments to understand your code, they need to reflect on what is wrong with the code.

Finally, for the content of the annotation, "why you did it" should be more important than "what you did".

4. Comment format.

File comments: Use the comment format automatically generated by Xcode.

//
//  AppDelegate.h
//  项目名称
//
//  Created by 开发者姓名 on 2018/6/8.
//  Copyright © 2018年 公司名称. All rights reserved.
//

Import comments: If there is more than one import statement, these statements are grouped. Comments for each group are optional .

// Framework
#import <UIKit/UIKit.h>

// Model
#import "WTUser.h"

// View
#import "WTView.h"

Method comment: After Xcode8, the shortcut key is automatically generated (option + command + /).

/**
* <#Description#>
* @param application <#application description#>
* @param launchOptions <#launchOptions description#>
* @return <#return value description#>
*/
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

/// <#Description#> 
/// @param application <#application description#>
/// @param launchOptions <#launchOptions description#>
/// @return <#return value description#>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

Code block comments: A single line starts with "// + space or /// + space", and multiple lines use "/* */".

/// + Spaces and /* */ comment methods will prompt when calling

Code structure and layout

  • Declaration file: The order of methods should be consistent with the order of the implementation file. Use "#pragma mark -" to group methods as needed.
  • Implementation file: Methods must be grouped with "#pragma mark -". Priority before and after grouping: Lifecycle method > Public method > UI method > Data method > Event method > Private method (logical processing, etc.) > Delegate method > Partial Override method > Setter method > Getter method
#pragma mark - Lifecycle

- (instancetype)init {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)viewDidAppear:(BOOL)animated {}
- (void)viewWillDisappear:(BOOL)animated {}
- (void)viewDidDisappear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}
- (void)dealloc {}

#pragma mark - Public

- (void)refreshData {}

#pragma mark - UI

- (void)initSubViews {}

#pragma mark - Data

- (void)initData {}
- (void)constructData {}

#pragma mark - Event

- (void)clickButton:(UIButton *)button {}

#pragma mark - Private

- (CGFloat)calculateHeight {}

#pragma mark - UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {}

#pragma mark - Override

- (BOOL)needNavigationBar {}

#pragma mark - Setter

- (void)setWindow:(UIWindow *)window {}

#pragma mark - Getter

- (UIWindow *)window {}

Code Review

Line breaks, comments, method length, code duplication, etc. are all problems that are detected by machines and do not need to be done by humans.

In addition to reviewing the degree of implementation of requirements and whether bugs have nowhere to hide, you should also pay more attention to the design of the code. For example, the degree of coupling between classes, the scalability and reusability of the design, whether certain methods can be extracted as interfaces, etc.

Guess you like

Origin blog.csdn.net/yezuiqingxin/article/details/120367074