继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

IOS编码规范

慕村9548890
关注TA
已关注
手记 1102
粉丝 227
获赞 987

一.总体原则

  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


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP