iOS-代码规范

一   命名规则编程

 

比较经常使用的变量命名法则有三种:驼峰命名法;下划线命名法;帕斯卡命名法数组

 

  • 驼峰命名法:用的最广的命名法,变量名经常使用此命名,命名由一个单词或多个单词组合而成,首字母小写其他单词首字母大写,如:“userName”;
    下划线命名法:每一个单词间使用下划线“_”分割,全部字母均小写,如:“user_name”;
    安全

  • 帕斯卡命名法:每一个单词的首字母均大写的一串字符,与“骆驼命名法”的区别在于前者的首字母大写,后者的首字母小写。如:“UserName”。app

  •  

 

1.项目命名规则框架

Xcode项目的命名我的推荐使用帕斯卡命名法则。IXueDaiide

 

2..类命名规则函数

帕斯卡命名法则。XHLoginViewController性能

咱们在构建应用程序时,颇有可能会有部分代码用于后续的项目,或者发布出去供他人使用,那么在别人使用你的类库或者你引用其余人的类库时颇有可能出现相同命名的状况,从而引起出“重复符号错误”(duplicate symbol error),为了不这种使人恼怒的状况发生,咱们应该习惯于给本身的类添加一个前缀,能够是公司名称的缩写,也能够是你我的姓名的缩写,还有多是框架名称的缩写,Xcode设置类前缀的地方在这里:测试

 

这里须要注意的是,推荐你们使用三个字母以上做为类的前缀,不少开发者都习惯于使用两个字母做为前缀(做者本人之前也是),可是苹果公司保留了两个字母做为类前缀的权利,你们能够发现苹果公司的类都是以框架名缩写而且都是两个字母做为前缀的,为了不苹果公司未来新发布的框架缩写和你的前缀发生冲突,因此采用三个字母以上的前缀命名方案。不光类名如此,类目及延展或者自定义的结构体等一样推荐使用前缀。动画

 

项目中添加plist类型文件,不要命名为info.plist,以防止和系统自带的文件重名,发生莫名其妙的问题;

 

3.变量命名规则

  • 普通变量(修饰+类型)
    1
    2

    @property (nonatomic, strong) UILabel *titleLabel; //表示*标题*的label,是*UILabel*类型
    @property (nonatomic, strong) UIButton *confirmButton; //表示*确认*的button,是*UIButton*类型

     

  • 若是是声明BOOL类型,建议在括号中重写get方法 

    1

    @property (nonatomic, readonly, getter = isKeyWindow) BOOL keyWindow;

     

     

  • NS_ENUM 和  NS_OPTIONS 宏来定义枚举类型

     
  • 在常量前边加上字母k做为标记
    static const NSTimeInterval kAnimationDuration = 0.3
  • 一些公开的常量一般使用类名做为前缀,一样是避免命名冲突而引起问题。案例: 
  • UIKIT_EXTERN NSString *const UIApplicationDidEnterBackgroundNotification// 常量命名

  • 通知命名 使用const修饰,以Notification结尾
    通知命名规则: [触发通知的类名] + [Did | Will] + [动做] + Notification
    错误示例:
    UIKIT_EXTERN NSString *const textFieldTextBeginEditingNotification;
    UIKIT_EXTERN NSString *const textFieldTextEndEditingNotification;
    正确操做:
    UIKIT_EXTERN NSString *const UITextFieldTextDidBeginEditingNotification;
    UIKIT_EXTERN NSString *const UITextFieldTextDidEndEditingNotification;

    ps:这里面须要注意的是变量名尽可能不要使用缩写,如咱们常常能够看到不少开发者习惯于把根视图控制器写成rootVC或者mainVC等等,而系统给咱们提供的倒是完整的命名:self.window.rootViewController,假如系统给咱们提供的是self.window.rootVC这种形式,以及其余命名方式也这样以非专业词汇的缩写命名,相信不少开发者会看的一头雾水。

 

4.宏命名规则

 

一般会把单词的全部字母大写,目的是为了告诉开发者这是一个宏,而不是一个普通的变量,固然若是宏的名称若是由多个单词组成,一般是每一个单词之间使用下划线分割开,如:“JXL_ABC_DEF”这种形式。

 

5.方法命名规则

大部分方法能够分为两类:要什么 和 作什么

  • 要什么表示取得某个对象,要以名词做为方法的开头如:“string”,“data”,“image”等,案例:

    - (NSRange)rangeOfString:(NSString *)searchString;  // 代表获取一个range
    - (UIImage *)imageNamed:(NSString *)name;

         

       

  • 作什么表示执行某种操做,要以动词做为方法开头
  • (NSArray<ObjectType> *)sortedArrayUsingSelector:(SEL)comparator;  // 代表目的是排序
    - (void)setUpNavBar

 

  • 若是该方法须要参数,每一个参数前最好添加参数提示。以下面两种代码对比。

    - (instancetype)init:(CGRect)frame;  // 糟糕的方法命名
    - (instancetype)initWithFrame:(CGRect)frame;  // 好的方法命名

 

  • 若是该方法须要多个参数,不能使用and这个单词链接参数

    1
    - (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
  • 一些表明过程监听的方法可能以“谁执行什么过程”这种形式命名,且动做发生以前一般使用“Will”,发生以后使用“Did”,询问是否发生使用“Should”。案例:
    1
    2
    3
    - (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;  //将要选择这一行cell
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; //已经选择这一行cell
    - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); //是否高亮(选择这一行cell)

 

  • 回调方法第一个参数是调用者
    1
    - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
    - (void)buttonTapped:(UIButton*)sender;

 

  • 类目方法:若是咱们在编码中使用类目给一些系统的类拓展方法,那么推荐给这些方法添加前缀,目的很简单,就是避免和这个类的私有方法或者未来系统可能拓展的方法出现方法名相同的冲突。如我当初在写夜间模式的demo的时候给UIView写了一个类目,拓展的方法名我使用了姓名的缩写“jxl”做为方法的前缀:
  • (void)jxl_setDayMode:(DAY_AND_NIGHT_MODE_BLOCK)dayMode nightMode:(DAY_AND_NIGHT_MODE_BLOCK)nightMode;   

 

 

  • 返回BOOL值得方法加前缀is,has

    1
    2

    - (BOOL)isEqualToString:(NSString *)aString;
    - (BOOL)hasPrefix:(NSString *)aString;

 

 

二 代码编写规范

1.引用头文件

类的头文件尽可能不要引用其余头文件,无需知道类的内部细节使用@class便可

 

2.使用类型常量替换#define预处理指令

在编写代码时,咱们经常使用#define去定义一个宏。咱们定义一个播放动画的时间为常量,可能这样定义:

#define ANIMATION_DURATION 1.0

上述预处理指令会把源代码中的 ANIMATION_DURATION 替换为1.0,能够达到效果,不过这样定义是存在问题的,定义出来的常量没有类型信息,没法一眼看出表明的是一个时间,可读性差,并且若是把这个宏定义放在头文件中的话,那么引入了这个头文件的代码,其 ANIMATION_DURATION 都会被替换,若是有人定义了常量值,这将致使应用程序中的常量值不一致。一种更好的处理方案是使用类型常量替换掉相应的#define预处理指令。

  • 外部不可见:

    .m文件中:

static const NSTimeInterval kAnimationDuration = 1.0;// 命名规则:不被外部访问时 k+变量名

static修饰:意味着仅在此编译单元(.m文件)中可见;const修饰:若是试图修改值,编译器就会报错;static const:两者都使用,编译器的处理效果和#define同样,把遇到的变量替换为常值。

  • 外部可见:
    有些时候是须要向外部公开某个常量的,For example,在使用通知中心的时候,你须要向其余对象派发通知,监听者须要知道监听的事件,这个事件的名称咱们一般写成一个外界可见的常值变量,这样的话,监听者无需知道实际字符串的值,只须要以常值变量来做为本身监听的事件名称,系统的 UIApplicationDidEnterBackgroundNotification, UIApplicationWillEnterForegroundNotification等都是这样作的:
    在.h文件中:

UIKIT_EXTERN NSString *const MyClassNameActionNameNotification; // 命名规则:类名+事件名+Notification   

ps:从右至左解读,“一个常量,而这个常量是一个指针,指向NSString对象”。
在.m文件中:

NSString *const MyClassNameActionNameNotification = @"MyClassNameActionNameNotification";
  • 本例中的写法为:
    在.h文件中:

UIKIT_EXTERN const NSTimeInterval MyClassNameAnimationDuration;// 命名规则:类名+变量名

在.m文件中:

const NSTimeInterval MyClassNameAnimationDuration = 0.3;

 

3.用枚举表示设置或状态

当咱们想要表示某一种设置的多个选项或者多种状态时,推荐使用枚举。枚举的意义原本就是将一些表示某一种设置或者状态的数字转化成方便开发者阅读的形式,极大的提升了可读性。以上面其余的命名规则话题中提到过的枚举为例:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) { 
        UIViewAnimationTransitionNone,   // 枚举值命名
        UIViewAnimationTransitionFlipFromLeft, 
        UIViewAnimationTransitionFlipFromRight, 
        UIViewAnimationTransitionCurlUp, 
        UIViewAnimationTransitionCurlDown, 
    };

 

这个枚举含有5个值,分别表示了5种动画状态,若是系统以“0”,“1”,“2”等这样的数字来表示状态的话,做为开发者想要知道每一个数字表明什么样的动画效果只能一个一个的去测试,而且须要记住每个数字表明什么状态,方便之后使用,那么我相信绝大多数开发者都会疯掉的。

 

4.协议的签定格式

协议的签定推荐下面这种写法,这种写法的好处是你签定了什么协议一目了然,并且后面填写注释看起来也会更加舒服。

@interface FooViewController ()
<
    UITableViewDataSource, // 你的注释
    UITableViewDelegate // 你的注释
>
@end

 

 

5.代码整理

为了让你的代码更整洁,你须要将你的代码作好归类整理,例如一个ViewController实现文件里的代码多是这样:

#pragma mark - Life Cycle

// Methods...
#pragma mark - UITableViewDataSource

// Methods...

#pragma mark - UITableViewDelegate

// Methods...

#pragma mark - CustomDelegate

// Methods...

#pragma mark - Private Methods

// Methods…(.m中声明)

#pragma mark - Public Methods

// Methods…(.h中声明)

#pragma mark - Getters and Setters

// Methods...

#pragma mark - Notification

//Methods...

#pragma mark - Event Response

// Methods...

#pragma mark - Request Methods

//Methods...

 

6.实例变量声明时变量名前面加下划线“_”,局部变量不用加

错误示范:
@implementation ViewController
{
    UIButton *authCodeBtn;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *_loginBtn = [[UIButton alloc] init];
}

好的习惯:
@implementation ViewController
{
    UIButton *_authCodeBtn;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *loginBtn = [[UIButton alloc] init];

}

 

7.场景需求:在继承中,凡是要求子类重写父类的方法必须先调用父类的这个方法进行初始化操做;建议:父类的方法名后面加上NS_REQUIRES_SUPER; 子类重写这个方法就会自动警告提示要调用这个super方法,示例代码

// 注意:父类中的方法加`NS_REQUIRES_SUPER`,子类重写才有警告提示

- (void)prepare NS_REQUIRES_SUPER;

8.成员变量 setter&getter

一些刚刚入门的iOS开发者老是纠结在给属性赋值或者取值的时候,到底是直接操做成员变量仍是经过使用setter或者getter进行赋值取值操做,关于这个问题不少人的观点也不相同,从性能上来讲,因为Objective-C的消息机制使用setter和getter效率要比直接操做成员变量的效率低,从内存方面来讲,setter和getter都有内存的处理,因此使用起来更安全,一个折中的办法是除Lazy Loading对象外,赋值时使用setter,取值时直接操做成员变量,这样既保障了内存的安全,又提高了效率,并且一般咱们的取值操做又多于赋值操做。

 

尽量使用点语法访问属性,可是访问其余实例对象使用括号

1
2
3
4
5

view.backgroundColor = [UIColor redColor];
[UIApplication sharedApplication].delegate; //推荐

[view setBackgroundColor:[UIColor redColor]];
UIApplication.sharedApplication.delegate; //不推荐

 

 

9.定义属性尽量写全参数 

1
@property (nonatomic, readwrite, copy) NSString *name;
  • 若是是内部使用的属性, 需定义成该类的私有属性(写在.m文件的class extension里)
  • 对于拥有Mutable子类型的对象, 例如NSString NSArray NSDictionary NSDictionary, 必定要定义成copy属性 
  • 尽可能不要暴露mutable类型的对象在public interface, 建议在.h定义一个Inmutable类型的属性, 而后在.m的get函数里面返回一个内部定义的mutable变量
  • 不要出现混合声明,尽量都使用@property声明

10.使用字面量语法替换等价方法

字面量语法其实是一种“语法糖”(syntactic sugar),以一种很是简单快捷的方式能建立对象,使咱们开发者编程更高效,更愉悦。目前Objective-C支持的字面量语法的类有NSString,NSNumber, NSArray, NSDictionary。使用字面量语法的好处:

  • 使用字面量语法能够缩减源代码长度,没有多余语法成分,提升可读性;

  • 在使用字面量语法建立数组时,若是数组元素对象中有nil,则会抛出异常,其效果等于先建立一个数组,而后把方括号内的全部对象都加到这个数组中。抛出的异常会是这样:

***Terminating app due to uncaught exception
‘NSInvalidArgumentException', reason:’***
-[__NSPlaceholderArray initWithObjects:count:] : attempt to
insert nil object from objects[0]'

 

案例:

NSArray *arrayA = [NSArray arrayWithObjects:object1object2object3, nil];

NSArray *arrayB = @[object1object2object3];

NSNumber *isHide = @NO;
NSNumber *errorCode = @404;

针对上面代码进行分析,若是object2=nil;,arrayA数组能够建立,但只有object1一个对象,由于“+ (instancetype)arrayWithObjects:”方法会一次处理各个参数,直到发现nil为止,而arrayB会抛出异常,这个特性使咱们更容易发现程序中存在的问题,提升了安全性。ps:字典跟数组同样,一旦有值为nil,也会抛出异常,并且建立时的“键”“值”顺序和咱们正常说的“键值”顺序同样(正常初始化为“值”“键”),便于阅读。

使用字面量语法的缺点:使用字面量建立都是不可变对象,若是想建立可变对象须要复制一份:

NSMutableArray *mutableArray = [@[@1, @2, @3] mutableCopy];

 

 

11.判断nil或者YES/NO

1
2
3
4
5
6
7
8
if (obj) 
  { 
    //... 
  }
if (!obj) 
  { 
    //... 
  }

 

 

12.BOOL类型赋值 

错误示例:
Bool isAdult;
if (age > 18){
  isAdult = YES;
else {
  isAdult = NO;
}
//
好的习惯
Bool isAdult;
isAdult = age > 18;

1


BOOL isAdult = age > 18;

13. 复杂的条件判断

若是判断较为复杂,尽量写到一个方法里 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


if ([self canDeleteAccount:account])
  { //... }
/**
 method
*/
- (BOOL)canDeleteAccount:(account)
{
        if (account.balance == 0  || account.owner.isDead == YES || account.isCancel == YES)
  {
         return YES;
        }
 else
       {
         return NO;
 }
}

14.嵌套判断

错误示例:
if(userName.length){
     if (passWord.length) {
            //能够登陆
        }
}
好的习惯:
if(!userName.length){ return; };
if(!passWord.length){ return; };

1

2
3


if (!user.account) return NO;
if (!user.password) return NO;
return YES;

15.加载xib

加载xib名称使用 NSStringFromClass()

1


[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([UserListCell class]) bundle:nil] forCellReuseIdentifier:ID];
 

16.判断if书写方式 

1
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) return 40;
    if (indexPath.row == 1) return 50;
    if (indexPath.row == 2) return 60;
    return 44;
}

 

 

17.对于参数不少的函数,参数各占一行,并以冒号对齐,如:

1
- (void)exampleFunctionWithPara:(int)para1
                    anotherPara:(int)para2
                   theThirdPara:(int)para3
                  theFourthPara:(int)para4
{
    ...
}
1


[self exampleFunctionWithPara:1
                  anotherPara:2
                 theThirdPara:3
                theFourthPara:4
                 theFifthPara:5];
 

18.运算符号间要留有合适的间隔

好的习惯:
sum = value1 + value2;//瞬间 高大上
UILable *lbl = [[UILable alloc] init] ;

 

19.字典构造时的注意点 : 先后要留有一个空格

错误示范:
 NSDictionary *attributs = @{                       
    NSForegroundColorAttributeName:[UIColor orangeColor],
    NSFontAttributeName:[UIFont systemFontOfSize:12]
    };
正确示例:
  NSDictionary *attributs = @{                       
    NSForegroundColorAttributeName : [UIColor orangeColor],
    NSFontAttributeName : [UIFont systemFontOfSize:12]
    };

 

 

 

20.对一些相同的东西避免写死,特别是控件的frame 不便于修改

错误示例:
 UITextField *phoneTf = [[UITextField alloc] initWithFrame:CGRectMake(10010040)];
 [self.view addSubview:phoneTf];
 UITextField *passwordTf = [[UITextField alloc] initWithFrame:CGRectMake(1011010040)];
 [self.view addSubview:passwordTf];
//
正确的方式:
 CGFloat margin = 10;
 CGFloat width = 100;
 CGFloat height = 40;
 UITextField *phoneTf = [[UITextField alloc] initWithFrame:CGRectMake(margin, 0, width, height)];
 [self.view addSubview:phoneTf];
 UITextField *passwordTf = [[UITextField alloc] initWithFrame:CGRectMake(margin, CGRectGetMaxY(phoneTf.frame) + margin, width, height)];
 [self.view addSubview:passwordTf];

 

21.避免循环引用

若是【block内部】使用【外部声明的强引用】访问【对象A】, 那么【block内部】会自动产生一个【强引用】指向【对象A】

若是【block内部】使用【外部声明的弱引用】访问【对象A】, 那么【block内部】会自动产生一个【弱引用】指向【对象A】

__weak typeof(self) weakSelf = self;
dispatch_block_t block = ^{
   [weakSelf doSomething]; // weakSelf != nil
// preemption, weakSelf turned nil
[weakSelf doSomethingElse]; // weakSelf == nil
};
最好这样调用:
__weak typeof(self) weakSelf = self;
myObj.myBlock = ^{

__strong typeof(weakSelf) strongSelf = weakSelf;

if (strongSelf) {
     [strongSelf doSomething]; // strongSelf != nil
// preemption, strongSelf still not nil(抢占的时候,strongSelf 仍是非 nil 的)
[strongSelf doSomethingElse]; // strongSelf != nil }
else { // Probably nothing... return;
}
};

weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是由于一旦进入block执行,就不容许self在这个执行过程当中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。

22.建议:

用CGSizeZero 代替 CGSizeMake(0,0);

CGRectZero代替CGRectMake(0, 0, 0, 0);

CGPointZero代替CGPointMake(0, 0)

 

23.在导航控制中,或它的子控制器,设置导航栏的标题应该用self.navigationItem.title = @“标题”而不建议self.title = @“标题”;

相关文章
相关标签/搜索