Skip to content

Instantly share code, notes, and snippets.

@HoneyLuka
Created June 4, 2020 02:59
Show Gist options
  • Save HoneyLuka/ff42329a719a414ecb9233958e29c4f1 to your computer and use it in GitHub Desktop.
Save HoneyLuka/ff42329a719a414ecb9233958e29c4f1 to your computer and use it in GitHub Desktop.

一、命名规范

1、统一要求

含义清楚,尽量做到不需要注释也能了解其作用,若做不到,就加注释,使用全称,不使用缩写。

2、类名

大驼峰式命名:每个单词的首字母都采用大写字母

MFHomePageViewController

3、私有变量

私有变量放在 .m 文件中声明

4、property变量

  • 小驼峰式命名:第一个单词以小写字母开始,后面的单词的首字母全部大写

  • 属性的关键字推荐按照 原子性,读写,内存管理的顺序排列。

  • Block、NSString属性应该使用copy关键字

typedef void (^ErrorCodeBlock) (id errorCode, NSString *msg);

// 注释
@property (nonatomic, strong) UIView *headerView;

// 将block拷贝到堆中
@property (nonatomic, copy) ErrorCodeBlock errorBlock;   

@property (nonatomic, copy) NSString *userName;    

5、宏和常量命名

  • 对于宏定义的常量

    • #define 预处理定义的常量全部大写,单词间用 _ 分隔
    • 宏定义中如果包含表达式或变量,表达式或变量必须用小括号括起来。
  • 对于类型常量

    • 对于局限于某编译单元(实现文件)的常量,以字符k开头,例如kAnimationDuration
    • 对于定义于类头文件的常量,外部可见,则以定义该常量所在类的类名开头,例如EOCViewClassAnimationDuration, 仿照苹果风格,在头文件中进行extern声明,在实现文件中定义其值
// 宏定义的常量
#define ANIMATION_DURATION 0.3
#define MY_MIN(A, B) ((A)>(B)?(B):(A))

// 局部类型常量
const NSTimeInterval kAnimationDuration = 0.3;

// 外部可见类型常量
// EOCViewClass.h
extern const NSTimeInterval EOCViewClassAnimationDuration;
extern NSString * const EOCViewClassStringConstant;  // 字符串类型

// EOCViewClass.m
const NSTimeInterval EOCViewClassAnimationDuration = 0.3;
NSString * const EOCViewClassStringConstant = @"EOCStringConstant";

6、Enum

  • Enum类型的命名与类的命名规则一致

  • Enum中枚举内容的命名需要以该Enum类型名称开头

  • NS_ENUM定义通用枚举,NS_OPTIONS定义位移枚举

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown,
};

typedef NS_OPTIONS(NSUInteger, UIControlState) {
    UIControlStateNormal       = 0,
    UIControlStateHighlighted  = 1 << 0,
    UIControlStateDisabled     = 1 << 1,
};

7、Delegate

  • delegate做后缀,如<UIScrollViewDelegate>

  • optional修饰可以不实现的方法,用required修饰必须实现的方法

  • 使用didwill通知Delegate已经发生的变化或将要发生的变化。

  • 类的实例必须为回调方法的参数之一

    • 回调方法的参数只有类自己的情况,方法名要符合实际含义
    • 回调方法存在两个以上参数的情况,以类的名字开头,以表明此方法是属于哪个类的
@protocol UITableViewDataSource <NSObject>

@required

// 回调方法存在两个以上参数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

@optional

// 回调方法的参数只有类自己
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;              // Default is 1 if not implemented

@protocol UITableViewDelegate <NSObject, UIScrollViewDelegate>

@optional

// 使用`did`和`will`通知`Delegate`
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

8、方法

  • 方法名用小驼峰式命名

  • 方法名不要使用new作为前缀

  • 不要使用and来连接属性参数,如果方法描述两种独立的行为,使用and来串接它们。

  • 方法实现时,如果参数过长,则令每个参数占用一行,以冒号对齐。

  • 一般方法不使用前缀命名,私有方法可以使用统一的前缀来分组和辨识

  • 方法名要与对应的参数名保持高度一致

  • 表示对象行为的方法、执行性的方法应该以动词开头

  • 返回性的方法应该以返回的内容开头,但之前不要加get,除非是间接返回一个或多个值。

  • 可以使用情态动词(动词前面can、should、will等)进一步说明属性意思,但不要使用do或does,因为这些助动词没什么实际意义。也不要在动词前使用副词或形容词修饰

// 不要使用 and 来连接属性参数
- (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes;    // 推荐
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;    // 反对

// 表示对象行为的方法、执行性的方法
- (void)insertModel:(id)model atIndex:(NSUInteger)atIndex;
- (void)selectTabViewItem:(NSTableViewItem *)tableViewItem

// 返回性的方法
- (instancetype)arrayWithArray:(NSArray *)array;

// 参数过长的情况
- (void)longMethodWith:(NSString *)theFoo
                  rect:(CGRect)theRect
              interval:(CGFloat)theInterval
{
    //Implementation
}

// 不要加get
- (NSSize)cellSize;  // 推荐
- (NSSize)getCellSize;  // 反对

// 使用情态动词,不要使用do或does
- (BOOL)canHide;  //推荐
- (BOOL)shouldCloseDocument;  //推荐
- (BOOL)doesAcceptGlyphInfo;  //反对

二、代码注释规范

优秀的代码大部分是可以自描述的,我们完全可以用代码本身来表达它到底在干什么,而不需要注释的辅助。

但并不是说一定不能写注释,有以下三种情况比较适合写注释:

  • 公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)。

  • 涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。

  • 容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。

除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。

最后,对于注释的内容,相对于“做了什么”,更应该说明“为什么这么做”。

1、import注释

如果有一个以上的import语句,就对这些语句进行分组,每个分组的注释是可选的。

// Frameworks
#import <QuartzCore>;

// Models
#import "NYTUser.h"

// Views
#import "NYTButton.h"
#import "NYTUserView.h"

2、属性注释

写在属性之后,用两个空格隔开

@property (nonatomic, readwrite, strong) UIView *headerView;  // 注释    

3、方法声明注释:

一个函数(方法)必须有一个字符串文档来解释,除非它:

  • 非公开,私有函数。

  • 很短。

  • 显而易见。

而其余的,包括公开接口,重要的方法,分类,以及协议,都应该伴随文档(注释)

方法的注释使用Xcode自带注释快捷键:Commond + Option + /

4、代码块注释

单行的用// + 空格开头,多行的采用/* */注释

5、TODO

使用// TODO: 说明 标记一些未完成的或完成的不尽如人意的地方

三、代码格式化规范

1、指针*位置

定义一个对象时,指针*靠近变量

NSString *userName;

2、方法的声明和定义

- 、+和返回值之间留一个空格

3、代码缩进

  • 不要在工程里使用 Tab 键,使用空格来进行缩进。在 Xcode > Preferences > Text Editing 将 Tab 和自动缩进都设置为 4 个空格

  • Method与Method之间空一行

  • 一元运算符与变量之间没有空格、二元运算符与变量之间必须有空格

!bValue
fLength = fWidth * 2;

- (void)sampleMethod1;

- (void)sampleMethod2;

4、对method进行分组

使用#pragma mark -method进行分组

#pragma mark - Life Cycle Methods
- (instancetype)init
- (void)dealloc

- (void)viewWillAppear:(BOOL)animated
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
- (void)viewDidDisappear:(BOOL)animated

#pragma mark - Override Methods

#pragma mark - Intial Methods

#pragma mark - Network Methods

#pragma mark - Target Methods

#pragma mark - Public Methods

#pragma mark - Private Methods

#pragma mark - UITableViewDataSource  
#pragma mark - UITableViewDelegate  

#pragma mark - Lazy Loads

#pragma mark - NSCopying  

#pragma mark - NSObject  Methods

5、大括号写法

  • 对于类的method:左括号另起一行写(遵循苹果官方文档)

  • 对于其他使用场景(if,for,while,switch等): 左括号跟在第一行后边

- (void)sampleMethod
{
    BOOL someCondition = YES;
    if(someCondition) {
        // do something here
    }
}

6、property变量

@property (nonatomic, readwrite, strong) UIView *headerView;  // 注释   

四、编码规范

1、if语句

  • 须列出所有分支(穷举所有的情况),而且每个分支都须给出明确的结果。
var hintStr;
if (count < 3) {
  hintStr = "Good";
} else {
  hintStr = "";
}
  • 不要使用过多的分支,要善于使用return来提前返回错误的情况,把最正确的情况放到最后返回。

推荐:

if (!user.UserName) return NO;
if (!user.Password) return NO;
if (!user.Email) return NO;

return YES;

不推荐这样:

BOOL isValid = NO;
if (user.UserName)
{
    if (user.Password)
    {
        if (user.Email) isValid = YES;
    }
}
return isValid;

  • 条件过多,过长的时候应该换行。条件表达式如果很长,则需要将他们提取出来赋给一个BOOL值,或者抽取出一个方法
if (condition1 && 
    condition2 && 
    condition3 && 
    condition4) {
  // Do something
}

BOOL finalCondition = condition1 && condition2 && condition3 && condition4
if (finalCondition) {
  // Do something
}


if ([self canDelete]){
  // Do something
}

- (BOOL)canDelete
{
    BOOL finalCondition1 = condition1 && condition2
    BOOL finalCondition2 =  condition3 && condition4

    return condition1 && condition2;
}
  • 每个分支的实现代码都须被大括号包围
if (!error) {
  return success;
}

2、for语句

  • 不可在for循环内修改循环变量,防止for循环失去控制。
for (int index = 0; index < 10; index++){
   ...
   logicToChange(index)
}

  • 在有返回值的方法里消除break:将break转换为return立即返回
func hasBadProductIn(products: Array<String>) -> Bool {

    var result = false    
    for level in products {
        if level.hasPrefix("bad") {
            result = true
            break
        }
    }
   return result
}

3、Switch语句

  • 每个分支都必须用大括号括起来
switch (integer) {  
    case 1: {
        // ...  
    }
        break;  
    case 2: {  
        // ...
    }
        break;
    default: {
        // ...  
        break; 
    }
}


4、函数

  • 一个函数只做一件事(单一原则)

每个函数的职责都应该划分的很明确(就像类一样)。

dataConfiguration()
viewConfiguration()

// 不推荐
void dataConfiguration()
{   
   ...
   viewConfiguration()
}

  • 对于有返回值的函数(方法),每一个分支都必须有返回值
int function()
{
    if (condition1) {
        return count1
    } else if(condition2) {
        return count2
    } else {
        return defaultCount
    }
}

// 不推荐
int function()
{
    if (condition1) {
        return count1
    } else if(condition2) {
        return count2
    }
}

  • 对输入参数的正确性和有效性进行检查,参数错误立即返回
void function(param1,param2)
{
      if(param1 is unavailable){
           return;
      }

      if(param2 is unavailable){
           return;
      }

     //Do some right thing
}
  • 如果在不同的函数内部有相同的功能,应该把相同的功能抽取出来单独作为另一个函数

  • 将函数内部比较复杂的逻辑提取出来作为单独的函数

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment