[iOS 知识总结一] 关于代码规范

作者:工程材料    来源:未知    发布时间:2019-12-18 19:31    浏览量:

开发中的规范

  1. 统一命名规范

    命名尽量采用英文名称

    名称一定要反映真实的功能含义

  2. Controller控制器的命名

    采用驼峰命名法,首字母大写,Controller结尾

    例如: PXYTradeViewController

  3. Util工具类的命名

    采用驼峰命名法,首字母大写,Helper结尾

    例如:PXYStringHelper

  4. View界面类的命名

    采用驼峰命名法,首字母大写,View结尾

    例如:PXYTradeView

  5. Plugin插件类的命名

    采用驼峰命名法,PXYPlugin开始,6位功能号结尾

    例如:PXYPlugin100001

  6. Delegate协议接口类的命名

    采用驼峰命名法,首字母大写,Delegate结尾

    例如:PXYServerDelegate

  7. Service业务服务类的命名

    采用驼峰命名法,首字母大写,Service结尾

    例如:PXYTradeService

  8. 注释规范

    复杂的函数需要写注释,但是那些对于通用的函数就不用写注释了,反而会污染代码,注释的话统一用系统的快捷键来创建

  9. 代码中尽量不要有冗余的代码,当时调试时候注释的代码,之后需要把不用的注释代码删除,文件中尽量删除没用的变量和函数,多余的换行

  10. 在头文件中说明下这个类的作用

  11. 打印日志的时候 禁止使用NSLog 要使用自定义的Log,避免Log 上生产

  12. 代码里面禁止使用 #if 0 #elif 1 #endif 之类的 难以阅读调试

  13. 发通知的Name,缓存的Key 要用宏或者静态变量,不要直接使用字符串

  14. 不要有无谓的空格 换行之类的

清晰

既清晰又简洁的命名最好,但以清晰为主,不要用单词简写(非常常用除外,苹果列出的可以接受的简写:链接),尽量使用全称。命名应符合OC标准,让人一看就知道是什么意思,不要让人有疑问,使用英文而不是拼音。

工程中的文件层次

  1. 首先全部要用真实目录,不然表面上层级很分明,其实还是乱的一团
  2. 对于项目文件夹用业务模块来区分,因为是团队模块开发,这样子可以缩小问题域;对于模块的复用迁移也是很方便的
  3. 合理的目录分层,尽量多去做分层,把文件安排在应该在的地方,这样子对于管理也是比较方便的,比如弄个ThirdComponents 专门来管理第三方框架的,这样子就很清楚的可以知道这个工程到底用到了什么第三方框架,便于升级和管理
  4. 把Resources 文件夹打散,下落到每个模块文件夹中,这样子在模块的迁移的时候不会遗漏资源文件,而且对于每个模块资源大小也可以控制,可以检测到到底是哪个业务模块将包大小搞大了
  5. 尽量不要创建Common、Core 这些文件夹,因为这样子会导致很多工具类在里面,没人维护,宁愿抽出来做组件都好

方法声明和定义

Tip
- + 和返回类型之间须使用一个空格,参数列表中只有参数之间可以有空格。

方法应该像这样:

- (void)doSomethingWithString:(NSString *)theString { 
    ...
}

星号前有空格。当写新的代码时,要与先前代码保持一致。

如果一行有非常多的参数,将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。

- (void)doSomethingWith:(GTMFoo *)theFoo 
                   rect:(NSRect)theRect
               interval:(float)theInterval { 
    ...
}

当第一个关键字比其它的短时,保证下一行至少有 4 个空格的缩进。这样可以使关键字垂直对齐,而不是使用冒号对齐:

- (void)short:(GTMFoo *)theFoo 
    longKeyword:(NSRect)theRect 
    evenLongerKeyword:(float)theInterval { 
    ...
}

正文

为什么团队开发总是要扯到代码规范、Code Review 上面呢,因为每个开发者经验不同,经历的环境不同,所养成的编码习惯自然也不相同

现在基本上项目都由多人协作开发的,而且随着人员的流动,一个项目代码可能经由几个开发者之手,这个时候,如果没有代码规范,这个项目代码维护性、阅读性这些方面就会比较堪忧了

对于代码并没有什么对与错之分,只要完成了项目的功能,你是用什么方式完成的并不是很重要。毕竟软件是给用户使用的,用户并不会关心你的软件底层实现逻辑,只要能帮忙解决用户遇到的问题就好了。但是代码却有鲁棒性、松耦合/紧耦合这些区别,所以代码分好代码、坏代码 = =

好了 不扯这些虚的了,讲一下关于iOS 项目工程中的一些规范问题吧

日志规范

只在debug的时候输出日志, release的时候不输出。减少性能损耗和安全隐患。
在-Prefix.pch(pch全称是“precompiled header”,也就是预编译头文件,该文件里存放的工程中一些不常被修改的代码,比如常用的框架头文件)文件中添加

#ifdef DEBUG
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define DLog(...)
#endif

其中DLog在开发中根据项目进行命名,在实际使用日志的时候,都使用DLog,不使用NSLog。并且该自定义日志可以输出具体的类和行数。实际开发中,只需要用 DLog(...) 就可以在输出需要信息的同时, 还输出所在类、 函数(方法)名以及行数。可以用这种方法很快找到输出所在的位置。

参考:http://zh-google-styleguide.readthedocs.io/en/latest/google-objc-styleguide/contents/
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146-SW1
http://www.csdn.net/article/2015-06-01/2824818-objective-c-style-guide/1

最后

其实这种代码规范上网一搜一大把,这里就简单列举我遇到的问题,需要详细的上网搜一下就好了。规范定下来了,接下来就是执行了,一开始可能会不太习惯,但是坚持了一段时间,就会感觉还好了。

花括号书写

  • 左大括号前不换行
  • 左大括号后换行
  • 右大括号前换行
  • 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行

例如,如果右大括号后面是else或逗号,则不换行。

前言

关于代码规范这个话题就比较广泛,没有绝对的对与错,只有相对的好与坏。

每个人都有自己的编码习惯,但是考虑到团队开发、项目后期的维护性上面,我觉得还是很有必要定一个代码规范的,不然项目越到后期越难维护

@public和 @private

Tip
@public和 @private访问修饰符应该以一个空格缩进。

与 C++ 中的 public, private以及 protected非常相似。

@interface MyClass : NSObject { 
 @public
  ... 
 @private 
  ...
}
@end

方法调用

Tip
方法调用应尽量保持与方法声明的格式一致。当格式的风格有多种选择时,新的代码要与已有代码保持一致。

调用时所有参数应该在同一行:

[myObject doFooWith:arg1 name:arg2 error:arg3];

或者每行一个参数,以冒号对齐:

[myObject doFooWith:arg1 
               name:arg2 
              error:arg3];

不要使用下面的缩进风格:

[myObject doFooWith:arg1 name:arg2 // some lines with >1 arg 
              error:arg3];

[myObject doFooWith:arg1 
               name:arg2 error:arg3];

[myObject doFooWith:arg1 
          name:arg2 // aligning keywords instead of colons 
          error:arg3];

方法定义与方法声明一样,当关键字的长度不足以以冒号对齐时,下一行都要以四个空格进行缩进。

[myObj short:arg1 
    longKeyword:arg2 
    evenLongerKeyword:arg3];

注释规范

行宽

尽量让你的代码保持在 80 列之内(或者100)。在xcode中的设置方式如下:

图片 1

image.png

Xcode工程

物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在本地文件系统体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。

文件头注释

文件头注释采用如下格式,该注释由xcode自动生成。如果你对其他人的原始代码作出重大的修改,请把你自己的名字添加到作者里面,并且填写相应的备注。

//
//  TestComment.m
//  TestVcard
//
//  Created by zhangyang on 2017/6/20.
//  Copyright © 2017年 myCompany. All rights reserved.
//

修改项目名称和公司名称的方法请参考下图。修改用户名请在系统偏好设置->用户账户中进行修改。

图片 2

image.png

块(闭包)

Tip
块(block)适合用在 target/selector 模式下创建回调方法时,
因为它使代码更易读。块中的代码应该缩进 4 个空格。

取决于块的长度,下列都是合理的风格准则:

  • 如果一行可以写完块,则没必要换行。
  • 如果不得不换行,关括号应与块声明的第一个字符对齐。
  • 块内的代码须按 4 空格缩进。
  • 如果块太长,比如超过 20 行,建议把它定义成一个局部变量,然后再使用该变量。
  • 如果块不带参数,^{ 之间无须空格。如果带有参数,^( 之间无须空格,但 ) { 之间须有一个空格。
// The entire block fits on one line.
[operation setCompletionBlock:^{ [self onOperationDone]; }];

// The block can be put on a new line, indented four spaces, with the
// closing brace aligned with the first character of the line on which
// block was declared.
[operation setCompletionBlock:^{ 
    [self.delegate newDataAvailable];
}];

// Using a block with a C API follows the same alignment and spacing
// rules as with Objective-C.
dispatch_async(fileIOQueue_, ^{ 
    NSString* path = [self sessionFilePath]; 
    if (path) { 
        // ... 
}});

// An example where the parameter wraps and the block declaration fits
// on the same line. Note the spacing of |^(SessionWindow *window) {|// compared to |^{| above.
[[SessionService sharedService] 
    loadWindowWithCompletionBlock:^(SessionWindow *window) { 
        if (window) { 
            [self windowDidLoad:window]; 
        } else { 
            [self errorLoadingWindow]; 
        } }];

// An example where the parameter wraps and the block declaration does
// not fit on the same line as the name.
[[SessionService sharedService] 
    loadWindowWithCompletionBlock:
        ^(SessionWindow *window) { 
            if (window) { 
                [self windowDidLoad:window]; 
            } else { 
                [self errorLoadingWindow]; 
            } 
        }];

// Large blocks can be declared out-of-line.
void (^largeBlock)(void) = ^{ 
    // ...
};
[operationQueue_ addOperationWithBlock:largeBlock];

一致性

做某件事的代码通常都叫这个名字,比如tag、setStringValue,那你也这么叫。

缩进

缩进统一为4个空格

普通变量名

对于静态的属性(int 或指针),尽量为变量起一个描述性的名字。不要担心浪费列宽,因为让新的代码阅读者立即理解你的代码更重要。例如:

  • 错误的命名:
int w;
int nerr;
int nCompConns;
tix = [[NSMutableArray alloc] init];
obj = [someObject object];
p = [network port];
  • 正确的命名:
int numErrors;
int numCompletedConnections;
tickets = [[NSMutableArray alloc] init];
userInfo = [someObject object];
port = [network port];

类成员变量

  • 如果只是单纯的private变量,最好声明在implementation里.
  • 如果是类的public属性,就用property写在.h文件里
  • 如果自己内部需要setter和getter来实现一些东西,就在.m文件的类目里用property来声明

类成员变量(写在implementation里面),以下划线开头,使用单词全名按顺序拼接方式,属性不需要,系统会自动生成下划线开头的成员变量。

用枚举表示状态、选项、状态码

项目中,用枚举来表示一系列的状态、选项和状态码。例如 iOS SDK 中表示 UICollectionView 滑动方向的枚举定义如下:

typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) {
    UICollectionViewScrollDirectionVertical,
    UICollectionViewScrollDirectionHorizontal
};

定义的枚举类型名称应以 2~3 个大写字母开头,而这通常与项目设置的类文件前缀相同,跟随其后的命名应采用驼峰命名法则,命名应准确表述枚举表示的意义,枚举中各个值都应以定义的枚举类型开头,其后跟随各个枚举值对应的状态、选项或者状态码。

对于需要以按位或操作来组合的枚举都应使用 NS_OPTIONS 宏来定义,例如 SDK 中:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

这样定义的选项能够以 按位或操作符 来进行组合,枚举中每个值均可启用或者禁用某一选项,在使用时,可以使用 按位与操作符 来检测是否启用了某一选项,如下:

UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | 
UIViewAutoresizingFlexibleHeight;

if (resizing & UIViewAutoresizingFlexibleWidth) {
    // UIViewAutoresizingFlexibleWidth is set
}

另外,我们可能使用 switch 语句时,会在最后加上 default 分支,但是若用枚举定义状态机,则最好不要使用 default 分支,因为如果稍后再加了一种状态,那么编译器就会发出警告,提示新加入的状态并未在 switch 分支中处理。假如写上了 default 分支,那么它就会处理这个新状态,从而导致编译器不发出警告,用 NS_ENUM 定义其他枚举类型时也要注意此问题。例如在定义代表 UI 元素样式的枚举时,通常要确保 switch 语句能正确处理所有样式。

协议名

Tip
类型标识符和尖括号内的协议名之间,不能有任何空格。

这条规则适用于类声明、实例变量以及方法声明。例如:

@interface MyProtocoledClass : NSObject<NSWindowDelegate> { 
    @private id<MyFancyDelegate> delegate_;
}
- (void)setDelegate:(id<MyFancyDelegate>)aDelegate;
@end

书写规范

方法注释

xcode 8已经集成了VVDocument,直接使用该注释格式。所有非系统方法都需要添加注释,包括在头文件和实现文件中。在相应的方法上方使用快捷键alt+command+/,即可生成该方法的注释:

/**
 注册用户

 @param userId 用户id
 @param userPwd 用户密码
 @return 0 成功  其他 失败
 */
- (NSInteger)registerUser:(NSString *)userId userPwd:(NSString *)userPwd;

注意,如果该方法所属的文件是他人创建的,需要添加上用户和日期。如果该方法不是与文件在同一时间创建的,也需要添加时间:

/**
 注册用户

 @author zhangyang
 @date 2017/06/20
 @param userId 用户id
 @param userPwd 用户密码
 @return 0 成功  其他 失败
 */
- (NSInteger)registerUser:(NSString *)userId userPwd:(NSString *)userPwd;

CGRect函数

当访问CGRect里的x, y, width, 或 height时,应该使用CGGeometry函数而不是直接通过结构体来访问。
引用Apple的CGGeometry:

在这个参考文档中所有的函数,接受CGRect结构体作为输入,在计算它们结果时隐式地标准化这些
rectangles。因此,你的应用程序应该避免直接访问和修改保存在CGRect数据结构中的数据。相反,
使用这些函数来操纵rectangles和获取它们的特性。

应该:

CGRect frame = self.view.frame;  
CGFloat x = CGRectGetMinX(frame);  
CGFloat y = CGRectGetMinY(frame);  
CGFloat width = CGRectGetWidth(frame);  
CGFloat height = CGRectGetHeight(frame);  
CGRect frame = CGRectMake(0.0, 0.0, width, height);  

不应该:

CGRect frame = self.view.frame;  
CGFloat x = frame.origin.x;  
CGFloat y = frame.origin.y;  
CGFloat width = frame.size.width;  
CGFloat height = frame.size.height;  
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size }; 

常量

常量名(如宏定义、枚举、静态局部变量等)应该以小写字母 k 开头,使用驼峰格式分隔单词,如:kInvalidHandle,kWritePerm。

类成员是界面元素的命名

命名方式为变量含义+视图后缀,采用小驼峰方式书写。

@property (nonatomic, strong) UIViewController *loginViewController;
@property (nonatomic, strong) UIView *loginHeaderView;
@property (nonatomic, strong) UILabel *loginLabel;
@property (nonatomic, strong) UIButton *loginBtn;

文件命名

Tip
文件名须反映出其实现了什么类 – 包括大小写。

文件的扩展名应该如下:

后缀 说明
.h C/C++/Objective-C 的头文件
.m Ojbective-C 实现文件
.mm Ojbective-C++ 的实现文件
.mm Ojbective-C++ 的实现文件
.cc 纯 C++ 的实现文件
.c 纯 C 的实现文件
  • 所有自定义的文件名称以项目工程开头命名,eg:“XP”、“ZJG”、“SZ”
  • 针对不同视图控制器,在末尾添加后缀,eg:
    • UIViewController 后缀添加“ViewController”
    • UIView 后缀添加“View”

单例模式

单例对象应该使用线程安全模式来创建共享实例。

+ (instancetype)sharedInstance {  
  static id sharedInstance = nil;  
  static dispatch_once_t onceToken;  
  dispatch_once(&onceToken, ^{  
      sharedInstance = [[self alloc] init];  
  });  
  return sharedInstance;  
}  

这会防止possible and sometimes prolific crashes。

空格的使用

if、else、for、switch、while等逻辑关键字与后面的语句留一个空格隔开。

if (isSuccess) { 
    // TODO while booleanVariable is true
} else { 
    // TODO else
}

运算符两边各用一个空格隔开。

int result = a + b; //Good, = 和 + 两边各用一个空格隔开
int result=a+b; //Bad,=和+两边没用空格隔开
for (int i = 0; i < 10; i++);   //good
for (int i=0;i<10;i++);   //bad

属性定义括号与前后各有一个空格,指针的*号紧靠变量名。如下所示:

@property (strong, nonatomic) UIWindow *window;

类构造方法

当类构造方法被使用时,它应该返回类型是instancetype而不是id。这样确保编译器正确地推断结果类型。

@interface Airplane  
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;  
@end  

类别名

Tip
类别名应该有两三个字母的前缀以表示类别是项目的一部分
或者该类别是通用的。类别名应该包含它所扩展的类的名字。

比如我们要基于 NSString 创建一个用于解析的类别,我们将把类别放在一个名为 GTMNSString+Parsing.h 的文件中。类别本身命名为 GTMStringParsingAdditions (是的,我们知道类别名和文件名不一样,但是这个文件中可能存在多个不同的与解析有关类别)。类别中的方法应该以 gtm_myCategoryMethodOnAString: 为前缀以避免命名冲突,因为 Objective-C 只有一个名字空间。

类名与包含类别名的括号之间,应该以一个空格分隔。

Init方法

Init方法应该遵循Apple生成代码模板的命名规则,返回类型应该使用instancetype而不是id。

- (instancetype)init {  
  self = [super init];  
  if (self) {  
      // ...  
  }  
  return self;  
}  

基本原则

异常

Tip
每个 @标签应该有独立的一行,在 @与 {}之间需要有一个空格,@catch
与被捕捉到的异常对象的声明之间也要有一个空格。

如果决定使用 Objective-C 的异常,那么就按下面的格式。不过最好先看看 避免抛出异常 了解下为什么不要使用异常。

@try { 
    foo();
}
@catch (NSException *ex) { 
    bar(ex);
}
@finally {
    baz();
}

编程实践

驼峰原则

大驼峰(UserNameLabel):每个单词首字母大写
小驼峰(userNameLabel):除第一个单词外,其它单词首字母大写

方法命名

方法名应遵守小驼峰原则,首字母小写,其他单词首字母大写,每个空格分割的名称以动词开头。执行性的方法应该以动词开头,小写字母开头,返回性的方法应该以返回的内容开头,但之前不要加get。

- (void)insertModel:(id)model atIndex:(NSUInteger)atIndex;
- (instancetype)arrayWithArray:(NSArray *)array;

接口注释

每个接口、类别以及协议应辅以注释,以描述它的目的及与整个项目的关系。使用如下格式:

/**
 添加描述

 @author zhangyang
 @date 2017/06/20
 */

可以将该段注释放在代码段中,如下图,我设置了快捷键为cmc。注意添加完成后需要修改描述和日期。

图片 3

变量和常量注释

下面几种情况下的常量和变量,都要添加注释说明,在上方添加注释。//和内容后面有一个空格。

  • 接口中定义的所有常量
  • 公有类的公有常量
  • 枚举类定义的所有枚举常量
  • 实体类的所有属性变量
// 用户名
@property (nonatomic, strong) NSString *userId;

//用户密码
@property (nonatomic, strong) NSString *userPwd;

类命名

类名(以及类别、协议名)应首字母大写,并以驼峰格式分割单词

  • 所有类名称以项目工程开头命名,eg:“XP”、“ZJG”、“SZ”
  • 针对不同视图控制器,在末尾添加后缀,eg:
    • UIViewController 后缀添加“ViewController”
    • UIView 后缀添加“View”
    • UIButton 后缀添加“Button”、“Btn”
    • UILabel 后缀添加“Label"

代码组织分类

在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:

#pragma mark - Lifecycle  
- (instancetype)init {}  
- (void)dealloc {}  
- (void)viewDidLoad {}  
- (void)viewWillAppear:(BOOL)animated {}  
- (void)didReceiveMemoryWarning {}  
#pragma mark - Custom Accessors  
- (void)setCustomProperty:(id)value {}  
- (id)customProperty {}  
#pragma mark - IBActions  
- (IBAction)submitData:(id)sender {}  
#pragma mark - Public  
- (void)publicMethod {}  
#pragma mark - Private  
- (void)privateMethod {}  
#pragma mark - Protocol conformance  
#pragma mark - UITextFieldDelegate  
#pragma mark - UITableViewDataSource  
#pragma mark - UITableViewDelegate  
#pragma mark - NSCopying  
- (id)copyWithZone:(NSZone *)zone {}  
#pragma mark - NSObject  
- (NSString *)description {} 

命名规范

变量命名

Tip
变量名应该以小写字母开头,并使用驼峰格式。

图片资源文件名

  • 模块+功能命名法(公共使用:common+功能)。公共模块主要包括统一的背景,导航条,标签,公共的按钮背景,公共的默认图等等;私有模块主要根据app的业务功能模块划分,比如用户中心,消息中心等。
  • 单词全拼,或者大家公认无歧义的缩写(如:nav,bg,btn等)

个人中心模块中我的消息按钮示例:personal_btn_my_message
公共模块搜索按钮示例:common_icon_search(或者common_search_icon)

Delegate命名

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

-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section

回调方法的参数只有类自己的情况,方法名要符合实际含义, 如:

-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView

以类的名字开头(回调方法存在两个以上参数的情况)以表明此方法是属于哪个类的, 如:

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

使用did和will通知Delegate已经发生的变化或将要发生的变化, 如:

-(NSIndexPath*)tableView:(UITableView*)tableView willSelectRowAtIndexPath:(NSIndexPath*)indexPath;
-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath;
上一篇:没有了
下一篇:没有了

相关新闻推荐

友情链接: 网站地图
Copyright © 2015-2019 http://www.kai-wang.com. AG亚游国际有限公司 版权所有