美文网首页
Objective-C编程规范

Objective-C编程规范

作者: 刘是丑 | 来源:发表于2018-11-16 15:17 被阅读12次

引言

背景

Apple官方的代码规范, 供补充参考:

目录

语言

使用美式英语

Preferred:

UIColor *myColor = [UIColor whiteColor];

Not Preferred:

UIColor *myColour = [UIColor whiteColor];

代码组织

使用#pragma mark -对function/protocol/delegate进行分组

#pragma mark - Lifecycle

- (instancetype)init {}
- (void)dealloc {}
- (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)privateMethod {}

#pragma mark - Protocol

#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

#pragma mark - NSCopying

- (id)copyWithZone:(NSZone *)zone {}

#pragma mark - NSObject

- (NSString *)description {}

如果private的方法过多

#pragma mark - Private

#pragma mark - AAAA

#pragma mark - BBBB

空格

条件表达式的括号 (如: if/else/switch/while), 左括号和表达式在同一行, 右括号另起一行

Preferred:

if (user.isHappy) {
  // Do something
} else {
  // Do something else
}

Not Preferred:

if (user.isHappy)
{
  // Do something
}
else {
  // Do something else
}

避免使用冒号对齐方式, 除非代码过长需要换行, 但是不要让block也同样保持冒号对齐

Preferred:

// blocks are easily readable
[UIView animateWithDuration:1.0f animations:^{
  // something
} completion:^(BOOL finished) {
  // something
}];

Not Preferred:

// colon-aligning makes the block indentation hard to read
[UIView animateWithDuration:1.0
                 animations:^{
                     // something
                 }
                 completion:^(BOOL finished) {
                     // something
                 }];
  • 签代理

Preferred:

@interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer>
@property (nonatomic, assign) id<ChooseProvinceViewControllerdDelegate> delegate;

Not Preferred:

@interface UIViewController : UIResponder<NSCoding,UIAppearanceContainer>
@property (nonatomic, assign) id <ChooseProvinceViewControllerdDelegate> delegate;

注释

  • 确实需要, 才加注释

  • 注释是用来解释why, 而不是what

Preferred:

// alignment attributes must have a secondViewAttribute
// therefore we assume that is refering to superview
// eg make.left.equalTo(@10)
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
    secondLayoutItem = self.firstViewAttribute.view.superview;
    secondLayoutAttribute = firstLayoutAttribute;
}

Not Preferred:

/**
 * show head image
 */
@property (nonatomic, strong) DDImageView *headView;

// im conversation done edit
- (void)imConversationDoneEdit;
  • 添加的注释要保持更新或删除

命名

  • 使用长且描述性强的命名方式

Preferred:

UIButton *settingsButton;

Not Preferred:

UIButton *setBut;
  • 在类名和常量名前加上3个字母的前缀(Core Data entity names去掉前缀)

需要统一整个工程的命名前缀, 因为OC里没有namespace, 工程越庞大后果越严重

  • 常量的命名采用"驼峰"的命名方式

Preferred:

static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3f;

Not Preferred:

static NSTimeInterval const fadetime = 1.7f;

成员的命名采用首字母小写的"驼峰"命名方式, 并且使用auto-synthesis方式声明成员, 而不是@synthesize方式

Preferred:

@property (nonatomic, copy) NSString *descriptiveVariableName;

Not Preferred:

id varnm;

命名时注意存储数据的数据结构发生改变的时候,会不会引起命名的改变。

Preferred:

@property (nonatomic, strong) NSArray *actionSheetItems;
@property(nullable, nonatomic, copy) NSArray<UIBarButtonItem *> *items;   

Not Preferred:

@property (nonatomic, strong) NSArray *actionSheetItemArray;

图标资源命名

模块名+功能名+[状态],( [ ] : 表明可选)

图标在Xcode里面的名称需要与图标的物理命名保持一致

Preferred:

addressbook_isvnetwork // 指代没有状态的图标名

addressbook_call_normal 
addressbook_call_highlight  // 指代有多种状态的图标名

conference_member_delete_normal
conference_member_delete_highlight  // 指代功能名较长且有多种状态的图标名

下划线

使用self.方式来访问对象的成员(参考点语法), 从视觉上就可以区分出哪些是本对象成员

注意:

  • 在初始化方法(init, initWithCoder:等), dealloc以及setters/getters方法中中必须使用保留的_variableName

  • 局部变量不要用下划线开头! 因为下划线开头的命名方式是Apple保留的命名方式

方法

  • 方法类型(-/+)的后面有个空格

  • 每一个参数都要有描述

注意:

  • 不要在多个参数的描述中添加"and", 例如下面的initWithWidth:height:方法

Preferred:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;

Not Preferred:

-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height;  // Never do this.

变量

变量命名要可读性强, 避免用单个单词的命名方式, 除了在for()循环里

指针的星号和变量名连在一起, 例如: NSString *text, 而不是NSString* text, NSString * text

避免直接使用_viriableName方式访问对象的成员, 除了在初始化方法(init, initWithCoder:等), dealloc方法以及setters/getters方法中点语法

了解更多关于在初始化和dealloc中使用accessor方法, 请参考这里.

Preferred:

@interface RWTTutorial : NSObject

@property (nonatomic, copy) NSString *tutorialName;
@property (nonatomic, copy) NSArray<UIBarButtonItem *> *items;

@end 

Not Preferred:

@interface RWTTutorial : NSObject {
  NSString *tutorialName;
}

如果变量是一个度量的话(如按时间长度或者字节数),那么最好把名字带上它的单位

Preferred:

NSString *durationOfSeconds;

Not Preferred:

NSString *duration;

属性

属性要按照先atomicity后storage的顺序, 这是为了和Apple的Interface Builder生成的代码保持一致.不用对齐,根据功能用空格实现分组

Preferred:

@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nullable, readonly, nonatomic, copy) NSString *tutorialName;

Not Preferred:

@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (nonatomic) NSString *tutorialName;

注意:

  • 控件的storage属性通常设置为weak, 而非strong

如果成员引用的对象是可变对象, 那么需要使用copy而非strong

Why?

因为即使成员的类型是NSString, 但实际传入的如果是NSMutableString的对象

该对象在你不知情的情况下, 仍然会被修改

Preferred:

@property (nonatomic, copy) NSString *tutorialName;

Not Preferred:

@property (nonatomic, strong) NSString *tutorialName;

点语法

点语法实际是对accessor方法的封装

了解更多点语法, 请参考这里

访问和操作对象成员, 推荐使用点语法; Bracket notation is preferred in all other instances.

Preferred:

NSInteger arrayCount = [self.array count];
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

Not Preferred:

NSInteger arrayCount = self.array.count;
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

Object Literals

使用Object Literals(@)方式来快速创建NSString, NSDictionary, NSArray, NSNumber实例

注意:

  • 如果传给NSArray, NSDictionary的值是nil会导致crash

Preferred:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;

Not Preferred:

NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];
  • 容器类型最好标明存储数据的类型

Preferred:

@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForVisibleRows;

- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView ;
  • 在数组和字典中, 使用索引和关键字来获取数据

Preferred:

NSString *name = names[1];
NSString *product = [productManagers valueForKey:@"kate"]

Not Preferred:

NSString *name = [names objectAtIndex:1];
NSString *product = [productManagers objectForKey:@"@kate"]

常量

使用static而非#define来声明常量; unless explicitly being used as a macro

用const修饰时,const右边的总是不能被修改.声明时不用对齐,根据功能用空格实现分组

Preferred:

static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";

static CGFloat const RWTImageThumbnailHeight = 50.0f;

Not Preferred:

#define CompanyName @"RayWenderlich.com"

#define thumbnailHeight 2.0f
  • 宏定义

使用大写字母,用_分割单词,宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来

不到万不得已不推荐使用宏定义像数字常量,通知的参数一般不推荐使用宏定义,推荐使用static const 的形式

Preferred:

#define SCREEN_RECT ([UIScreen mainScreen].bounds)

#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
  • 浮点数

使用浮点数在数值的后面加上f,用来区别double类型

Preferred:

static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3f;

Not Preferred:

static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3;

枚举

使用NS_ENUM()声明枚举, 因为它具有更强的类型检查机制

For Example:

typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
  RWTLeftMenuTopItemMain,
  RWTLeftMenuTopItemShows,
  RWTLeftMenuTopItemSchedule
};

当然你还是可以显式地设置枚举的值

typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
  RWTPinSizeMin = 1,
  RWTPinSizeMax = 5,
  RWTPinCountMin = 100,
  RWTPinCountMax = 500,
};

不要使用老式的枚举定义方式, 除非编写CoreFoundation C的代码

Not Preferred:

enum GlobalConstants {
  kMaxPinSize = 5,
  kMaxPinCount = 500,
};

Case表达式

Case表达式通常不需要加括号

Case内容有if判断句, for循环和局部变量时, 需要加上括号 (左括号的右边, break放在括号外). break与下一个case之间有空行.

switch (condition) {
  case 1:  {
     for () {
        // ...
     }
  }
     break;
  
  case 2: {
    if {
    //
    }
  }
    break;
  
  case 3:
    // ...
    break;
  
  default: 
    // ...
     break;
  }

多个Case表达式执行相同的逻辑, 使用fall-through方式(即删除表达式里的break)

此时, 需要加上注释说明注释

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

私有成员

私有属性要在类的实现中声明: 放在class extension(即匿名category)中

For Example:

@interface RWTDetailViewController ()

@property (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;

@end

布尔

Objective-C使用YESNO, 而truefalse只用在CoreFoundation, C或C++代码里

Since nil resolves to NO it is unnecessary to compare it in conditions. Never compare something directly to YES, because YES is defined to 1 and a BOOL can be up to 8 bits.

This allows for more consistency across files and greater visual clarity.

Preferred:

if (someObject) {}
if (![anotherObject boolValue]) {}

Not Preferred:

if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this
if (isAwesome == true) {} // Never do this

如果BOOL类型的属性是一个形容词, 那么可以去掉"is"前缀, 但get accessor中仍然需要保留前缀

@property (assign, getter=editable) BOOL editable;

例子来自Cocoa Naming Guidelines.

条件语句

条件语句要加上括号, 即使是一行语句, 否则会存在隐患: even more dangerous defect

Preferred:

if (!error) {
  return success;
}

Not Preferred:

if (!error)
  return success;

or

if (!error) return success;

三元运算符

如果可以使代码变得简洁和高效, 那么可以考虑使用三元运算符?:, 否则还是用if表达式

通常, 给变量赋值时, 可以考虑使用三元运算符?:

Preferred:

NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;

Not Preferred:

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

初始化方法

初始化方法要和Apple模板保持一致

- (instancetype)init {
  self = [super init];
  if (self) {
    // Do something
  }
  return self;
}

返回值类型是'instancetype'而不是'id'

关于instancetype, 请参考类构造方法

类构造方法

类构造方法返回值类型是'instancetype'而不是'id', 这样可以帮忙编译器推导出返回值得类型

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end

了解更多关于instancetype: NSHipster.com.

CGRect函数

要获取CGRectx, y, width, height, 不要直接访问结构体的成员, 而应该使用CGGeometry functions

Apple官方对于CGGeometry的解释:

All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.

Preferred:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0f, 0.0f, width, height);

Not Preferred:

CGRect frame = self.view.frame;

CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

Golden Path

如果把需要执行的逻辑比作"golden"或"happy" path

那么在判断条件不满足时, 直接return退出方法, 而不是判断条件满足时, 执行该Golden Path

Preferred:

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

Not Preferred:

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

错误处理

通过方法的返回值而不是返回的error引用, 来做错误处理的判断条件

Preferred:

NSError *error;
if (![self trySomethingWithError:&error]) {
  // Handle Error
}

Not Preferred:

NSError *error;
[self trySomethingWithError:&error];
if (error) {
  // Handle Error
}

单例

单例对象的实例化必须要确保线程安全

+ (instancetype)sharedInstance {
  static id sharedInstance = nil;

  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
  });

  return sharedInstance;
}

否则可能会出现这样的问题possible and sometimes prolific crashes.

换行

头文件中的方法, 使用冒号对齐的方式

Preferred:

 - (void)setViewWithHeadImageUrl:(NSString *)headImageUrl 
                            name:(NSString *)name 
                     phoneNumber:(NSString *)phoneNumber;

Not Preferred:

- (void)setViewWithHeadImageUrl:(NSString *)headImageUrl name:(NSString *)name phoneNumber:(NSString *)phoneNumber;

实现文件或方法调用时, 不用冒号对齐

Preferred:

[[MSDBHelper sharedInstance] updateCallRecordsWithNumber:number isPersonalContact:YES oldName:oldContact.displayName toNewName:@""];

Not Preferred:

[[MSDBHelper sharedInstance] updateCallRecordsWithNumber:number
                                       isPersonalContact:YES
                                                 oldName:oldContact.displayName
                                               toNewName:@""];

如果语句过长, 需要考虑代码的分解和优化

Preferred:

NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[ctpManager connectToHost:kHostNameCloudPhoneNoport onPort:kCTPPort userInfo:userInfo;
[userInfo setObject:[AccountLoginModel sharedInstance].userInfo.mobilephone forKey:CTP_AUTHUSER_USER_NAME, nil];
    

Not Preferred:

 [ctpManager connectToHost:kHostNameCloudPhoneNoport onPort:kCTPPort userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[AccountLoginModel sharedInstance].userInfo.mobilephone, CTP_AUTHUSER_USER_NAME, nil];

头文件

  • 自定义头文件放在系统头文件的前面

  • 按功能分开,加空格以示区分

  • 按字母表排序

Preferred:

#import "DailModel"

#import "DialView.h"

#import "DialViewController.h"

#import <QYCTPManager/CTPManager.h>

Not Preferred:

#import <QYCTPManager/CTPManager.h>
#import "DialView.h"
#import "DailModel"
#import "DialViewController.h"
  • 不要引入无关的头文件

  • 尽量使用向前声明取代引入,这样不仅可以缩减编译时间,而且还能降低彼此依赖程度

版权声明

Preferred:

//  MSDetailRecordsViewController.m
//  CloudPhone
//
//  Created by chenguang (guochenguang@qiyoukeji.com) on 15-12-6.
//  Copyright (c) 2015年 QIYOU Ltd. All rights reserved.
//

Not Preferred:

//  MSDetailRecordsViewController.m
//  chenguang
//
//  Created by chenguang on 15-12-6.
//  Copyright (c) 2015年 CloudPhone. All rights reserved.
//

Xcode Project

  • Xcode Group要和文件系统里的文件夹关联起来

  • 代码不仅要按照类型分组, 也要按照功能和特性进行分组

  • 如果可以的话, 打开Build Settings里的"Treat Warnings as Errors"选项

  • 并且尽可能多的打开additional warnings

如果需要忽略特定的warning, 请参考Clang's pragma feature.

其他Objective-C编程规范

相关文章

  • iOS代码规范

    禅与 Objective-C 编程艺术 《Google Objective-C 风格指南》 《苹果官方编码规范》

  • 代码规范

    苹果代码规范 禅与 Objective-C 编程艺术 (Zen and the Art of the Object...

  • iOS-代码规范

    Objective-C编码规范 概要 Objective-C 是一门面向对象的动态编程语言,主要用于编写iOS和M...

  • Objective-C编程规范

    引言 背景 Apple官方的代码规范, 供补充参考: The Objective-C Programming La...

  • Objective-C编程规范

    引言 一直以来都是在谈如何开发, 如开发的小技巧小经验 今天为什么突然说起编程规范来了呢? 因为在我看来, 编程规...

  • Objective-C 编程规范

    命名规范 1.【强制】 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。 反例: _nam...

  • 我使用的编程规范

    我在使用的C、Objective-C、C++编编程时,有一套自己的规范,本文是我规范的基本内容,有些规范,比如空格...

  • Objective-C编程代码规范

    留白和格式 空格vs.制表符 我们使用空格缩进。不要在代码中使用制表符。你应该将编辑器设置成自动将制表符替换成空格...

  • Objective-C编程代码规范

    前言 为了降低程序员之间的沟通成本,遵守代码规范显得尤为重要。当拿到别人的代码的时候,如果对方写的天花乱坠,你一定...

  • OC代码规范总结

    Objective-C Coding Guidelines In Chinese Objective-C编码规范,...

网友评论

      本文标题:Objective-C编程规范

      本文链接:https://www.haomeiwen.com/subject/gbemfqtx.html