iOS代码开发详细规范

2017-06-07  本文已影响52人  麟young

一、命名

1.属性对象和局部变量(采用小驼峰命名法)

UI视图对象

变量名称 + (Label / Button / Cell / TableView / WebView / ScrollView / CollectionView / ImageView / TabBar / TextField ...)
如:UILabel *titleLabel;
特殊类型:UIBarButtonItem -> ButtonItem.

非UI视图对象

VC: name + (VC / TableVC / CollectionVC / PageVC)
如: UIPageViewController *firstPageVC;
特殊类型:

数据类型: name + 类型(Integer / Int / Long / Float / Double / Number / String / Array / MutableArray / IndexPath / Date / Error...)
如:NSArray *dataArray;
特殊类型:

注意:1.局部变量可不遵守变量名字后面加数据类型这一规则。 2.Bool类型以is、has、can等作为前缀,或以ing、ed等作后缀。

其他:

注意:尽量为每个变量取有意义的名字,即“name”,但若确实没有name的情况下,直接采用如下形式,如:UITableView *tableView;

2.实例变量

实例变量采用小驼峰命名法的基础上,以下划线“_”作为前缀,如:
UIButton *_loginButton;

3.常量 和 宏常量

普通常量:以小写字母“k”开头的驼峰命名法,如:static NSString *const kMovieCellHeight;
通知常量:一般形式为[ 触发通知的类名] + [Did 或 Will] + [ 动作 ] + Notification ;
宏常量:全部大写,中间用下划线“_”作间隔,如:#define TARGET_OS_IOS
注意:

4.枚举类型名称 和 枚举变量名称

定义枚举类型:采用OC风格定义枚举类型,举个栗子:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {  
    UIViewAnimationTransitionNone,         //默认从0开始  
    UIViewAnimationTransitionFlipFromLeft,  
    UIViewAnimationTransitionFlipFromRight,  
    UIViewAnimationTransitionCurlUp,  
    UIViewAnimationTransitionCurlDown,  
};

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  
};

其中:    
1.枚举类型名称采用首字母大写的驼峰命名法,并且添加相关类作为前缀    
(本例中相关的类为UIView)
2.枚举值命名要添加枚举类型名作前缀    
(本例中枚举值前缀分别为UIViewAnimationTransition和UIViewAutoresizing)

枚举变量名称和类型名称基本保持一致的情况下,尽量增强其可阅读性,采用小驼峰命名法,如:

UIViewAnimationTransition animationTransitionType; 

5.方法名

采用小驼峰命名法,做到见其名知其含义,本身具有充分的解释性,拒绝无意义命名。
例外情况:

6.类名 和 协议名

类名:“统一的类名前缀” + “功能模块简称”(可省略) + “描述该类的词或词组” + 控件类型名尾缀(如Cell,ViewController,View等,可省略)
协议名:类名 + “Delegate”
都采用大驼峰命名法。

7.分类的名称 和 分类中方法名

分类的名称和类名的命名方式基本一致,只是前缀由开发者指定。
但分类中的方法名,需使用分类的前缀小写形式 + “_”的形式,如:

@interface NSDate (MOLocalDate)

/// 获取根据时间间隔按照系统时间计算的日期,seconds:单位(秒)
+ (NSDate *)mo_dateWithTimeIntervalSince1970:(NSTimeInterval)seconds;    


8.Assets.xcassets文件命名

1.功能模块使用的资源,使用功能模块代码资源同样的文件夹结构(一级或多级)和名称。
2.公共或通用资源文件夹取名为“Common”或“Normal”
3.图片资源命名方式采用全部小写和分割线分隔的形式,前缀为功能模块名称或缩写,如:
common_navigation_bar_back

9.功能模块内文件夹命名

一般情况下,各功能模块下的文件夹命名包括但不限于以下命名:

10.Xib或Storyboard中命名

总结:命名尽量不要用单词简写,注重保持语义明确;名称保持一致性,避免同样语义出现多种单词如“number”和“count”;命名风格统一,遵守统一的的命名规则。

二、风格

1.关于空格与空行

@property (nonatomic, copy, readonly) NSString *titleString;    
小括号左右两边各用一个空格与外部隔开;
星号“*”左边留有一个空格。
@interface MOCalendarService : NSObject
冒号左右各用一个空格隔开
/// 分类
@interface NSDate (MOLocalDate)
/// 延展(类目)
@interface ClassName () <UICollectionViewDelegate,    
 UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

- 小括号左右两边各用一个空格与外部隔开
- 协议列表中,逗号后留有一个空格

2.注释

总结:

3.Mark分区

建议开发时使用Xcode右下角的代码快捷(Code Snippet Library)方式保存和提取常用文件Mark分区结构。
针对VC:

#pragma mark - View Lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self buildingUI];
    [self makeViewConstraints];
    [self bindViewModel];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}

#pragma mark - buildingUI
- (void)buildingUI {
}

#pragma mark - Make Constraints
- (void)makeViewConstraints {
}

#pragma mark - bindViewModel
- (void)bindViewModel {
}

#pragma mark - Delegate Methods

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Notification Event

#pragma mark - Property Set

#pragma mark - Property Get

针对ViewModel:

#pragma mark - Life Cycle
- (instancetype)init {
    self = [super init];
    if (self) {
        [self binding];
    }
    
    return self;
}

#pragma mark - Bind
- (void)binding {
    
}

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Property Set

#pragma mark - Property Get


针对TableViewController:

#import "<#ControllerName#>.h"

// static NSString *const <#cellNameId#> = @"<#cellId#>";

@interface <#ControllerName#> () <UITableViewDelegate, UITableViewDataSource>

// @property (nonatomic, strong) <#ViewModolClass#> *viewModel;
@property (nonatomic, strong) UITableView *tableView;

@end

@implementation <#ControllerName#>

#pragma mark - View Lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self buildingUI];
    [self makeViewConstraints];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}

#pragma mark - buildingUI
- (void)buildingUI {
    self.title = @"<#TitleName#>";
}

#pragma mark - Make Constraints
- (void)makeViewConstraints {
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
}

#pragma mark - UITableViewDataSource && UITableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return <#countInteger#>;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 44.f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 0.1f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 0.1f;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#cellNameId#>];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:<#cellNameId#>];
    }
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    // do something
    
}

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Notification Event

#pragma mark - Property Set

#pragma mark - Property Get
// - (<#ViewModolClass#> *)viewModel {
//  if (!_viewModel) {
//      _viewModel = [[<#ViewModolClass#> alloc] init];
//  }
//  return _viewModel;
//}

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        // _tableView.tableFooterView = [[UIView alloc] init];
        // _tableView.showsVerticalScrollIndicator = NO;
        // _tableView.contentInset = UIEdgeInsetsMake(0, 0, 50, 0);
        [self.view addSubview:_tableView];
    }
    return _tableView;
}

@end


针对Cell:

#pragma mark - Init Method
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self makeViewConstraints];
    }
    return self;
}

#pragma mark - 约束布局
- (void) makeViewConstraints {
}

#pragma mark - Public Methods

#pragma mark - Private Methods

#pragma mark - Property Set

#pragma mark - Property Get

针对自定义View:

#pragma mark - Init Method
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self makeViewConstraints];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self makeViewConstraints];
    }
    return self;
}

#pragma mark - 约束布局
- (void) makeViewConstraints {
}

#pragma mark - Publich Methods

#pragma mark - Private Methods

#pragma mark - Property Set

#pragma mark - Property Get

4.对齐

/// 我是数组
NSArray *demoArray = @[@"Object-C",
                       @"Swift",
                       @"Python",
                       @(YES),
                       @(1)
                      ];
/// 我是字典
NSDictionary *demoDict = @{@"Object-C":@"Object-C",
                           @"Swift":@"Swift",
                           @"Python":@"Python",
                           @"BoolValue":@(YES),
                           @"Number":@(1)
                          };
其实,上面两个例子中,中括号和大括号的始末位置也进行了对齐。
/// 我是 if 语句
if (18 == self.age) {
    if (self.isGirl) {
        NSLog(@"Marry me!");
    } else {
        /// do something
    }
}

建议使用if语句时,直接选择苹果提供的快捷代码段。

三、开发习惯

1.Switch语句每个case后添加“{}”的习惯

- (void)sampleForSwitch {
    SampleEnum testEnum = SampleEnumTwo;
    switch(testEnum) {
        caseSampleEnumUndefined: {
            // do something
            break;
        }
        caseSampleEnumOne: {
            // do something
            break;
        }
        caseSampleEnumTwo: {
            // do something
            break;
        }
        default: {
            NSLog(@"WARNING: there is an enum type not handled properly!");
            break;
        }
    }
}

2.先建立实体文件夹,后在工程中引入

3.条件语句

简单条件判断推荐使用三目运算符“? :”,如:

result = object ? : [self createObject];

注意这里“?”后写成空格,会直接返回object的情况,不建议用下面这种形式:
result = object ? object : [self createObject];    


4.Bool赋值

简单的条件判断后赋布尔值的逻辑,可以省略if语句,如:

BOOL isAdult = age > 18;

而不是:
BOOL isAdult;
if (age > 18) {
    isAdult = YES;
}
else {
    isAdult = NO;
}

5.拒绝魔术数字和字符串

举个例子:

/// 反例1 “Nissan”字符串突然横空出现
if (carName == "Nissan")
/// 反例2 “18”是个什么意思~
if (age > 18) { ... }

推荐下面的方式:
/// 用枚举类型代替字符串类型(由于是数字类型比较,编译速度比字符串类型更为高效)
if (car == Car.Nissan)
/// 提前为魔术数字定义常量,同时方便添加注释(即明确数字含义后再继续写代码)
const int adultAge = 18; 
if (age > adultAge) { ... }

6.readonly属性

不需要修改属性的地方,心怀添加readonly的念想。

7.copy属性

对于有可变类型子类的数据类型,使用copy属性防止数据被更改,如NSString、NSArray、NSDictionary。

8.使用字面量方式初始化数据,增强可读性

9.复杂判断简单化

if (job.JobState == JobState.New
    || job.JobState == JobState.Submitted
    || job.JobState == JobState.Expired
    || (job.JobTitle && job.JobTitle.length) {
    ....
}

可以被优化为:
/// 首先将上面整体提炼为一个方法
if ([self canDeleteJob:job]) { ... }        
/// 方法内对条件判断进行拆分
- (BOOL)canDeleteJob:(Job *)job {
    BOOL invalidJobState = job.JobState == JobState.New
                          || job.JobState == JobState.Submitted
                          || job.JobState == JobState.Expired;
    BOOL invalidJob = job.JobTitle && job.JobTitle.length;

    return invalidJobState || invalidJob;
}

10.嵌套判断平行化

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

可以被优化为:

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

return YES;

11.回调方法加调用者的习惯

如经典的:

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

添加调用者tableView作为参数,方便信息传递和区分调用者。

12.NSTimer、观察者、通知、网络请求等注意在Dealloc或viewWillDisappear:方法中移除。同一个文件中,移除的顺序应和创建的顺序保持一致,方便后期维护(注意:工程中使用RAC或已经封装好的方法不需要作移除操作)。

13.访问或操作NSArray、NSDictionary等对象时,注意判断对象和对象内要访问的元素是否为nil。

14.虽然很简单,但尽量不用new方法而是统一采用Cocoa规范中的[[ClassName alloc] init]方法

15.Delegate使用weak属性修饰

@property (nonatomic, weak) delegate;

16.提交代码前保证无warning和error

17.随时注释别人或未来的自己有可能看不懂的代码

18.逻辑捋清楚前不要写代码,因为一定会重写。

四、设计思想

1.精简

2.分工明确

3.团队一致性

上述所有,共同自律,提高整体效率。

上一篇下一篇

猜你喜欢

热点阅读