Objective-C编程规范

2018-11-16  本文已影响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;

注释

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;

需要统一整个工程的命名前缀, 因为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.方式来访问对象的成员(参考点语法), 从视觉上就可以区分出哪些是本对象成员

注意:

方法

注意:

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;

注意:

如果成员引用的对象是可变对象, 那么需要使用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实例

注意:

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

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

其他Objective-C编程规范

上一篇下一篇

猜你喜欢

热点阅读