为了账号安全,请及时绑定邮箱和手机立即绑定

IOS编码规范

标签:
iOS

一.总体原则

  1. 需求是暂时的,只有变化才是永恒的,面向变化编程,而不是面向需求编程。

  2. 不要过分追求技巧,降低程序的可读性

  3. 简洁的代码可以让bug无处藏身。要写出明显没有bug的代码,而不是没有明显bug的代码

  4. 先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。

二.编码规范

1.if表达式写法

推荐:

if (!error) {    return success;
}

不推荐:

if (!error)    return success;

和:

if (!error) return success;
  • 变量和常量比较
    推荐:

[myValue isEqual:@42];

不推荐:

[@42 isEqual:myValue];
  • nil和BOOL值检查
    推荐:

if (someObject) { ...if (![someObject boolValue]) { ...if (!someObject) { ...

不推荐:

if (someObject == YES) { ... // Wrongif (myRawValue == YES) { ... // Never do this.if ([someObject boolValue] == NO) { ...
  • 避免嵌套if语句,合理使用return可以避免增加代码复杂度,提高代码可读性。

推荐:

- (void)someMethod {    if (![someOther boolValue]) {        return;
    }    // Do something important}

不推荐:

- (void)someMethod {    if ([someOther boolValue]) {        // Do something important
    }
}

2.三元运算符

三元运算符 ? 应该只用在它能让代码更加清楚的地方。 一个条件语句的所有的变量应该是已经被求值了的。

推荐:

result = a > b ? x : y;

不推荐:

result = a > b ? x = c > d ? c : d : y;

当三元运算符的第二个参数(if 分支)返回和条件语句中已经检查的对象一样的对象的时候,下面的表达方式更灵巧:

推荐:

result = object ? : [self createObject];

不推荐:

result = object ? object : [self createObject];

3.Case语句

除非编译器强制要求,括号在 case 语句里面是不必要的。但是当一个 case 包含了多行语句的时候,需要加上括号。

switch (condition) {    case 1:        // ...
        break;    case 2: {        // ...
        // 多行语句需要加上括号
        break;
       }    case 3:        // ...
        break;    default:        // ...
        break;
}

有时候可以使用 fall-through 在不同的 case 里面执行同一段代码。一个 fall-through 是指移除 case 语句的 “break” 然后让下面的 case 继续执行。

switch (condition) {    case 1:    case 2:        // code executed for values 1 and 2
        break;    default:        // ...
        break;
}

当在 switch 语句里面使用一个可枚举的变量的时候,default 是不必要的。比如:

switch (menuType) {    case ZOCEnumNone:        // ...
        break;    case ZOCEnumValue1:        // ...
        break;    case ZOCEnumValue2:        // ...
        break;
}

4.函数

  • 单一原则

    一个函数只做一件事,每个函数的职责都应该划分的很明确。

    推荐:

dataConfiguration()
viewConfiguration()

不推荐:

void dataConfiguration(){   
   ...
   viewConfiguration()
}
  • 需要对参数的正确性和有效性进行检查

  • 对相同功能进行封装

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

三.命名规范

1.统一要求

推荐使用长的、描述性的方法和变量名。尽可能遵守Apple命名约定

推荐:

UIButton *settingsButton;

不推荐:

UIButton *setBut;

2.类名

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

例如:MainViewController

3.私有变量

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

  • 私有变量:以下划线开头 例如:NSString *_peopleName

4.属性

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

  • 关键字顺序:原子性、读写权限、内存管理

  • Block、NSString属性使用copy修饰

5.常量

  • 常量应该以驼峰法命名,并以相关类名作为前缀。

    推荐:

static const NSTimeInterval VAReportViewControllerFadeOutTime = 0.4;

不推荐:

static const NSTimeInterval fadeOutTime = 0.4;
  • 推荐使用常量来代替字符串字面值和数字,这样能够方便复用,而且可以快速修改而不需要查找和替换。常量应该用 static声明为静态常量,而不要用 #define,除非它明确的作为一个宏来使用。
    推荐:

static const NSTimeInterval VAReportViewControllerFadeOutTime = 0.4;static NSString * const VAReportViewControllerCellIdentifier = @"VAReportViewControllerCell";

不推荐:

#define VAReportViewControllerFadeOutTime 0.4#define VAReportViewControllerCellIdentifier @"VAReportViewControllerCell"

对于外部可见的常量,在头文件中以这样的形式暴露给外部:

extern NSString *const VAReportViewControllerCellIdentifier;

并在实现文件中为其赋值。

只有公有的常量才需要添加命名空间作为前缀。尽管实现文件中私有常量的命名可以遵循另外一种模式,你仍旧可以遵循这个规则。

6.枚举

  • 命名规则和类的命名一致。

  • 枚举内容的命名需要以该枚举类型名称开头。
    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,    UIControlStateSelected     = 1 << 2        };

7.指定初始化方法和间接初始化方法

Objective-C 有指定初始化方法(Designated Initializer)和间接(Secondary Initializer)初始化方法的观念。designated 初始化方法是提供所有的参数,secondary 初始化方法是一个或多个,并且提供一个或者更多的默认参数来调用 designated 初始化的初始化方法。

  • 在你希望提供你自己的初始化函数的时候,需要遵循下列原则:

    正确的例子:

  1. 定义你的 Designated Initializer,确保调用了直接父类的Designated Initializer。

  2. 重写直接父类的Designated Initializer。调用你的新的Designated Initializer。

  3. 为新的Designated Initializer 进行文档注释。

#import <UIKit/UIKit.h>@interface VAMessageTableViewCell : UITableViewCell- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier VA_DESIGNATED_INITIALIZER;
- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name identifier:(NSString *)identifier;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier VA_UNAVAILABLE_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder *)aDecoder VA_UNAVAILABLE_INITIALIZER;
- (instancetype)init VA_UNAVAILABLE_INITIALIZER;@end
    #import "VAMessageTableViewCell.h"@interface VAMessageTableViewCell ()@property (nonatomic,copy) NSString *name;@property (nonatomic,strong) NSDate *date;@end@implementation VAMessageTableViewCell- (VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier {    //调用直接父类的designated initializer
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];    if (self) {
        _name = name;
        _date = date;
    }    return self;
}//Secondary Initializer- (VAMessageTableViewCell *)initWithName:(NSString *)name identifier:(NSString *)identifier {    return [self initWithName:name date:nil identifier:identifier];
}//重写直接父类的Designated Initializer- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {    return [self initWithName:nil date:nil identifier:reuseIdentifier];
}//重写直接父类的Designated Initializer- (instancetype)initWithCoder:(NSCoder *)aDecoder {    return [self initWithName:nil date:nil identifier:nil];
}@end

其中VA_DESIGNATED_INITIALIZERVA_UNAVAILABLE_INITIALIZER定义如下:

#define VA_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))#define VA_UNAVAILABLE_INITIALIZER __attribute__((unavailable("please use designated initializer")))

相关宏介绍:

__attribute__((objc_designated_initializer)):用来修饰类的designated initializer初始化方法,如果修饰的方法里没有调用父类的 designated initializer,编译器会发出警告。

__attribute__((unavailable)):可以用来修饰变量,方法,类和协议,表明不可用,如果使用,编译器会发出错误。同deprecated,可以添加说明。

8.方法

推荐:

- (__kindof People *)initWithName:(NSString *)name age:(NSInteger)age birthday:(NSDate *)birthday;

不推荐:

-(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age andBirthday:(NSDate *)birthday;

建议所有返回类的实例的类方法和实例方法使用__kindof ,不要使用id或者instancetype,原因如下:

  • id修饰

  1. 用id修饰返回值类型,不会在编译时进行类型判断。

  2. 返回值类型没有确切提示。

instancetype修饰

虽然会自动识别当前对象的类,但是仍然没有类型提示。

9.通知

当你定义你自己的 NSNotification 的时候你应该把你的通知的名字定义为一个字符串常量,就像你暴露给其他类的其他字符串常量一样。你应该在公开的接口文件中将其声明为 extern 的, 并且在对应的实现文件里面定义。

因为你在头文件中暴露了符号,所以你应该按照统一的命名空间前缀法则,用类名前缀作为这个通知名字的前缀。

同时,用一个 Did/Will 这样的动词以及用 "Notifications" 后缀来命名这个通知也是一个好的实践。

// Foo.hextern NSString * const ZOCFooDidBecomeBarNotification// Foo.mNSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";

四.注释规范

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

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

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

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

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

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

1.import注释

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

// Frameworks#import <QuartzCore>;// Models#import "NYTUser.h"// Views#import "NYTButton.h"#import "NYTUserView.h"

2.属性注释

使用//注释,在属性之后,用一个空格隔开。

@property (nonatomic,copy) NSString *name; //用户名

3.方法注释

使用Xcode快捷键command+option+/进行注释:

/**
 方法描述 @param name 参数描述 @param date 参数描述 @param identifier 参数描述 @return 返回值
 */- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier VA_DESIGNATED_INITIALIZER;

4.代码块注释

单行使用// 多行使用/**/

5.#pragma

  • #pragma mark -是一个在类内部组织代码并且帮助你分组方法实现的好办法。

    分离示范:

#pragma mark - Get#pragma mark - Set#pragma mark - Life Cycle- (void)viewDidLoad {
    [super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
}#pragma mark - Super Class#pragma mark - Event Responder#pragma mark - TableView Delegate DataSource

6.TODO MARK FIXME 标记

    //MARK:标记一下
    //TODO:通知即将要做
    //FIXME:你想要修改的bug



作者:一片姜汁
链接:https://www.jianshu.com/p/b3626ec5f1fd


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消