代码改变世界

iOS 代码规范

2015-11-28 12:40  jiangys  阅读(790)  评论(0编辑  收藏  举报

概述

命名的好坏在开发中往往也不怎么重视,毕竟差的命名也不会影响程序逻辑。但是不好的命名在大项目中带来的隐形维护成本是相当高的,这些在项目开始时可能还很难察觉,而后来会陷入前仆后继的维护困境中。我们往往非常重视项目逻辑的复杂性,却不能好好的把“简单”的命名做好。其实,如果简单的东西都做不好,那么做出再复杂的东西那也是垃圾。

命名规范

分类(类别)命名

与类命名相同,此外需添加要扩展的类名和“+”

举例:NSString+URLEncoding

协议(委托)命名

与类命名相同,此外需添加“Delegate”后缀

举例:ReplyViewDelegate

方法命名

首字母小写,之后每个单词首字母都大写

方法名使用动词短语

举例:- (void)setupPostWithValue:(int)value

“要什么”往往被胡乱命名为get开头的方法。首先get是一个动词,所以它还是“做什么”或者说“做的是要什么”。那么get方法不要用于返回对象,但它可用于参数。

- (XXItem *)getItemAtIndex:(NSUInteger)index                  //Bad!! 不规范的命名
- (XXItem *)itemAtIndex:(NSUInteger)index                     //Good, 命名清晰
- (void)getItem:(XXItem **)outItem atIndex:(NSUInteger)index  //比较符合规范,但第二种更好。

参数命名

首字母小写,之后每个单词首字母都大写

每个参数前要加参数的名称提示

举例:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender

对象命名

采用修饰+类型的方式

举例:

titleLabel    //表示标题的label,  是UILabel类型
confirmButton //表示确认的button, 是UIButton类型

图片命名

使用英文,全部小写 ,添加模块名作为前缀,避免冲突 。图片应该与类文件一样,按模块分组放置

举例:

Home(界面名称)_(功能属性简写+描述).png

如: home_btn_recommended.png 

或:

compose_mentionbutton_background@2x.png

compose_mentionbutton_background_highlighted@2x.png

分组命名

使用英文,首字母大写,之后每个单词首字母都大写

每个分组使用模块的名字

使用的开源库统一放在“Library”分组下

使用的公共组件统一放在“Common”分组下

使用资源统一放在"Resource"分组下,包括文件、音频、图片、证书等

编码规范 

编码规范简单来说就是为了保证写出来的代码具备三个原则:可复用, 易维护, 可扩展. 这其实也是面向对象的基本原则. 可复用, 简单来说就是不要写重复的代码, 有重复的部分要尽量封装起来重用。

判断nil或者YES/NO

推荐:

if (someObject) { ... } 
if (!someObject) { ... }

不推荐:

if (someObject == YES) { ...} 
if (someObject != nil) { ...}

if (someObject == YES)容易误写成赋值语句

条件赋值

推荐:

result = object ? : [self createObject];

不推荐:

result = object ? object : [self createObject];

如果是存在就赋值本身, 简洁。

BOOL赋值

推荐:

BOOL isAdult = age > 18;

不推荐:

BOOL isAdult;
if (age > 18)
{
    isAdult = YES;
}
else
{
    isAdult = NO;
}

拒绝死值

推荐:

if (car == Car.Nissan)
or
const int adultAge = 18; 
if (age > adultAge) { ... }

不推荐:

if (carName == "Nissan")
or
if (age > 18) { ... }

死值每次修改的时候容易被遗忘, 地方多了找起来就悲剧了. 而且定义成枚举或者static可以让错误发生在编译阶段. 另外仅仅看到一个数字, 完全不知道这个数字代表的意义. 纳尼?

复杂的条件判断

推荐:

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

不推荐:

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

清晰明了, 每个函数DO ONE THING!

嵌套判断

推荐:

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

不推荐:

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

一旦发现某个条件不符合, 立即返回, 条理更清晰。

所有的逻辑块必须使用花括号包围,即使条件体只需编写一行代码也必须使用花括号

推荐:

if (!error) {
    return success;
}

不推荐:

if (!error)
    return success;
...
if (!error) return success;

参数过多

推荐:

- (void)registerUser(User *user)
{
     // to do...    
}

不推荐:

- (void)registerUserName:(NSString *)userName
                password:(NSString *)password 
                   email:(NSString *)email
{
     // to do...
}

当发现实现某一功能需要传递的参数太多时, 就预示着你应该聚合成一个model类了...这样代码更整洁, 也不容易因为参数太多导致出错。

点标记语法

属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况使用方括号标记语法。

推荐:

view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

不推荐:

[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

枚举

枚举使用Objective-C方式来定义,枚举命名是 类前缀+枚举名,枚举值要加枚举名。

举例:

typedef NS_ENUM(NSUInteger, HGMachineState) {
    HGMachineStateNone,
    HGMachineStateIdle,
    HGMachineStateRunning,
    HGMachineStatePaused
};

常量

常量应该类名称+“骆峰式”单词大写。

推荐:

static const NSTimeInterval HGSignInViewControllerFadeOutAnimationDuration = 0.4;

不推荐:

static const NSTimeInterval fadeOutTime = 0.4;

常数是优于串联字符串或数字的,允许多个地方使用常用变量,可以迅速改变不需要查找和替换。常量应该声明为静态常量而不使用宏定义,除非明确需要定义一个宏。

推荐:

static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const CGFloat ZOCImageThumbnailHeight = 50.0f;

不推荐:

#define CompanyName @"Apple Inc."
#define magicNumber 42

单例模式

使用GCD代替使用sharedInstance,每次调用+ (id)sharedInstance函数都会付出取锁的代价。

推荐:

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

不推荐:

+ (instancetype)sharedInstance
{
    static id sharedInstance;
    @synchronized(self) {
        if (sharedInstance == nil) {
            sharedInstance = [[MyClass alloc] init];
        }
    }
    return sharedInstance;
}

代理

定义一个协议是 类名+Delegate,并要使用weak 弱指针,协议使用可选@optional

@class ZOCSignUpViewController;

@protocol ZOCSignUpViewControllerDelegate <NSObject>
@required
- (void)signUpViewController:(ZOCSignUpViewController *)controller didProvideSignUpInfo:(NSDictionary *);
@optional
- (void)signUpViewControllerDidPressSignUpButton:(ZOCSignUpViewController *)controller;
@end


@interface ZOCSignUpViewController : UIViewController

@property (nonatomic, weak) id<ZOCSignUpViewControllerDelegate> delegate;

@end

在发送委托代理方法之前,一定要先检查该委托方法已经被实现(否则会崩溃)。

if ([self.delegate respondsToSelector:@selector(signUpViewControllerDidPressSignUpButton:)]) {
    [self.delegate signUpViewControllerDidPressSignUpButton:self];
}

动画

推荐

[UIView animateWithDuration:1.0
                 animations:^{
                     // something
                 }
                 completion:^(BOOL finished) {
                     // something
                 }];

不推荐

[UIView animateWithDuration:1.0 animations:^{
    // something 
} completion:^(BOOL finished) {
    // something
}];

Pragma Mark

使用“#pargma mark - ”实现方法的分组。建议按下面几点单独分组:

1.功能分组

2.代理协议实现分组

3.重写父类方法

- (void)dealloc { /* ... */ }
- (instancetype)init { /* ... */ }

#pragma mark - View Lifecycle

- (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)zoc_privateMethod { /* ... */ }

#pragma mark - UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }

#pragma mark - ZOCSuperclass

// ... overridden methods from ZOCSuperclass

#pragma mark - NSObject

- (NSString *)description { /* ... */ }

抽象方法定义

在Componens中有很多的抽象类,因为Objective-C没有语法限定抽象类,所以我们约定一下抽象类的注释和声明方法。
在类注释中声明该类为抽象类,抽象方法必须为公开方法,并添加#pragma标记注释 
@interface TestClass : NSObject

#pragma mark - Abstract Method

//抽象方法
-(void)doWork;

@end

抽象类不允许被实例化,所以需要在init方法中添加判断代码

@implementation TestClass

- (id)init {
    if( [self class] == [TestClass class]) {
        @throw [NSException exceptionWithName:@"抽象类无法实例化" reason:@"抽象类无法实例化: TestClass" userInfo:nil];
    }
    else {
        self = [super init];
        if(self){
            
        }
        return self;
    }
}

#pragma mark - Abstract Method

- (void)doWork {
    @throw [NSException exceptionWithName:@"抽象方法没有被实现" reason:@"抽象方法没有被实现: doWork" userInfo:nil];
}

@end

 

参数链接:

https://github.com/objc-zen/objc-zen-book

http://www.cocoachina.com/ios/20151014/13678.html

http://www.cocoachina.com/ios/20151118/14242.html