iOS开发

XXXXObjective-C 编码规范

2018-07-16  本文已影响40人  松哥888

1.建议开git的feature分支开发

2.合入Duckbill分支的两人review

3.每一个提交都是让代码变整洁,遵守styleguide

4.对复杂的controller建议采用MVVM

5.鼓励写单元测试

6.鼓励结对编程

介绍

这份规范指南概括了XXXX使用 Objective-C 时所遵循的代码约定。关于这个编程语言的所有规范,如果这里没有写到,请参考苹果文档:

示例代码如下:

typedef NS_ENUM(NSInteger, StyleGuideFormat) {
    StyleGuideFormatLeft,
    StyleGuideFormatRight,
    StyleGuideFormatUp,
    StyleGuideFormatDown
};

static NSString * const StyleGuideDidChangedNotification = @"StyleGuideDidChangedNotification";
static NSString * const StyleGuideUserInfoKey = @"StyleGuideUserInfoKey";
static NSString * const StyleGuideInvalidFormatException = @"StyleGuideInvalidFormatException";

static const NSInteger StyleGuideTotalCount = 100;

@interface StyleGuide () <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> {
    NSArray *_defaultStyleGuides;
    NSString *_currentName;
}

@property (nonatomic, readonly, getter = isEditable) BOOL editable;
@property (nonatomic, readwrite) NSDictionary *userInfo;

@end

@interface StyleGuide (StyleGuideExtendedMethod)

- (BOOL)TT_isReady;

- (void)sortWithName:(NSString *)name;
- (NSString *)title;
- (NSString *)titleAsASCIIEncoding;

@end

@implementation StyleGuide

- (void)dealloc {
    [super dealloc];
}

- (instancetype)init {
    self = [super init];
    if (self != nil) {
        //Custom initialization
    }
    
    return self;
}

- (NSInteger)count {
    NSInteger keyCount = 10;
    NSInteger objectCount = 20;
    NSInteger total = keyCount + objectCount;
    
    return total;
}

@end

@implementation StyleGuide (ExtendedMethod)

- (BOOL)TT_isReady {
    BOOL isFinished = ([self hasFinished] ? YES : NO);
    if (isFinished) {
        
    } else {
        
    }
}

@end

格式

缩进

一个缩进使用 4 个空格,永远不要使用制表符(tab)缩进。

空行

不同的模块之间以空行相隔,这有助于视觉清晰度和代码组织性,有以下几种(示例代码参考本指南开头的代码):

变量

推荐:

NSString *name;
__weak NSString *name;
__block NSString *name;

反对:

NSString* name;
NSString*name;
NSString * name;

常量

推荐:

NSString * const Name = @"John";
const int TotalNum = 100;

运算符

程序块

方法的大括号和其他的大括号(if/else/switch/while 等等)始终和声明在同一行开始,在新的一行结束。

推荐:

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

方法头

推荐:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;

多参数方法

当一个方法包含多个参数时,可以考虑每行一个参数,以 : 对齐。

推荐:

[NSError errorWithName:
              withType:
              withCode:];

当方法名中存在名字片段过长或者过短而无法以 : 对齐时,建议从第二行开始每行缩进四个空格。

推荐:

[error short:
    keyName:
    lastLongKeyName:];

函数头

推荐:

NSData *UIImagePNGRepresentation(UIImage *image);
void CFRelease(CFTypeRef cf);

(), <>, {}, @[], @{}

: 两边以空格相连。如下:

推荐:

@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
@interface NSString () <NSCopying, NSMutableCopying, NSSecureCoding>

属性

按照原子性、可读性、拥有性、以及自定义 Accesor Method 方法顺序排列,默认值可省去。

推荐:

@property (nonatomic, readonly, copy, setter = setTitle) NSString *title;

命名

本部分大部分内容来自于Cocoa 编码指南,如果这里没有提及,请参考Cocoa 编码指南

采用 camel-casing 命名法:将每个单词的首字母大写然后拼接起来。

基本原则

前缀

推荐:

typedef NS_ENUM(NSInteger, UIScreenOverscanCompensation);

NSString *const UIScreenDidConnectNotification;

NSData *UIImagePNGRepresentation(UIImage *image);

@protocol UIAlertViewDelegate <NSObject>

类名通常应该由名词组成,并完全遵循 camel-casing

协议

协议名字通常根据其包含的方法而定,有以下几类:

变量

属性

属性遵从变量的命名方式,如果属性名字是形容词,需指定 get 访问器。

推荐:

@property (nonatomic, readonly, getter = isPlayable) BOOL playable;

枚举

枚举常量的名字应以枚举变量的名字为前缀,其命名方式为:枚举类型 + 枚举状态。枚举类型的命名方式为:类名 + 名词/词组。

推荐:

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

通知

通知命名方式:[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification

推荐:

NSSystemTimeZoneDidChangeNotification
MPMediaLibraryDidChangeNotification
UIKeyboardWillShowNotification

异常

异常命名方式:[Name of associated class] + [UniquePartOfName] + Exception。

推荐:

NSRangeException
NSInvalidArgumentException

键指键值对中的键,用在字典中。其命名方式:[Name of associated class] + [UniquePartOfName] + Key。

推荐:

UIKeyboardFrameBeginUserInfoKey
ALAssetLibraryUpdatedAssetsKey

方法

函数

函数命名法类似方法,除了两点:

<span id="jump_1">通用的缩写</span>

Abbreviation Meaning and comments
alloc Allocate.
alt Alternate.
app Application.
calc Calculate.
dealloc Deallocate.
func Function.
horiz Horizontal.
info Information.
init Initialize.
int Integer.
max Maximum.
min Minimum.
msg Message.
nib Interface Builder archive.
pboard Pasteboard (but only in constants).
rect Rectangle.
Rep Representation.
temp Temporary.
vert Vertical.

以下是一些在计算机领域比较知名的缩写:

ASCII

PDF

XML

HTML

URL

RTF

HTTP

TIFF

JPG

PNG

GIF

LZW

ROM

RGB

CMYK

MIDI

FTP

其他

注释

当需要的时候,注释应该被用来解释 为什么 特定代码做了某些事情。所使用的任何注释必须保持最新否则就删除掉。

通常应该避免一大块注释,代码就应该尽量作为自身的文档。

Warning的要求

除了第三方库,不能引入新的警告

类前置声明

通常引用一个类有两种办法:一种是通过#import方式引入;另一种是通过@class引入;在头文件中通过@class引入这个类作为一个类型使用,减少头文件的重复包,提升编译效率。在实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类。

头文件声明:

@class StyleGuide;

StyleGuide *styleGuide;

实现文件引入头文件:

#import @"StyleGuilde.h"

#pragma mark分组

一些类(尤其是一些控制器类)可能很长,方法和函数弹出菜单可以便于代码导航。此时加入#pragma 指令对代码进行逻辑组织很有效果。提高可读性

例如:

#pragma mark - Initialization

私有方法属性定义

外部不需使用的属性方法成员变量不能暴露到.h文件。在类的.m文件中,采用类别来实现私有方法

例如:.m文件中定义

@interface MyClass()
  - (void)privateMethod;
@end

小贴士

.语法

应该 始终 使用.语法来访问或者修改属性,除此之外,不得使用.语法。

推荐:

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

反对:

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

条件判断

条件判断主体部分应该始终使用大括号括住来防止出错,即使它可以不用大括号(例如它只需要一行)。这些错误包括添加第二行(代码)并希望它是 if 语句的一部分时。还有另外一种更危险的,当 if 语句里面的一行被注释掉,下一行就会在不经意间成为了这个 if 语句的一部分。此外,这种风格也更符合所有其他的条件判断,因此也更容易检查。

推荐:

if (isFinished) {
    return success;
}

反对:

if (isFinished)
    return success;

if (isFinished) return success;

三目运算符

三目运算符,? ,只有当它可以增加代码清晰度或整洁时才使用。单一的条件都应该优先考虑使用,多条件时通常使用 if 语句会更易懂。

推荐:

result = (a > b ? x : y);

反对:

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

错误处理

当引用一个返回错误参数(error parameter)的方法时,应该针对返回值,而非错误变量。一些苹果的 API 在成功的情况下会写一些垃圾值给错误参数(如果非空),所以针对错误变量可能会造成虚假结果。

推荐:

NSError *error;
if (![self trySomethingWithError:&error]) {
    // 处理错误
}

反对:

NSError *error;
[self trySomethingWithError:&error];
if (error != nil) {
    // 处理错误
}

init 和 dealloc

dealloc 方法应该放在@implementation的最上面,并且刚好在 @synthesize@dynamic 语句的后面。在任何类中,init 都应该直接放在 dealloc 方法的下面。

init 方法的结构应该像这样:

- (instancetype)init {
    self = [super init]; 
    if (self != nil) {
        // Custom initialization
    }

    return self;
}

字面量

每当创建 NSStringNSDictionaryNSArray,和 NSNumber 类的不可变实例时,应使用 @"", @{}, @[], @()方式生成实例。要注意 nil 值不能传给 NSArrayNSDictionary 字面量,这样做会导致崩溃。

推荐:

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

反对:

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 *buildingZIPCode = [NSNumber numberWithInteger:10018];

CGRect 函数

当访问一个 CGRectxywidthheight 时,应该使用CGGeometry 函数代替直接访问结构体成员。苹果的 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.

推荐:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);

反对:

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;

应该尽量避免使用宏定义,除非没有别的选择,通常应当使用常量和枚举,它们具有更强的类型检查,而更加安全。

推荐:

static const CGFloat ThumbnailHeight = 50.0f;

反对:

#define ThumbnailHeight 2.0f

枚举类型

当使用 enum 时,建议使用新的基础类型规范,因为它具有更强的类型检查和代码补全功能。现在 SDK 包含了一个宏来鼓励使用使用新的基础类型 - NS_ENUM()

推荐:

typedef NS_ENUM(NSInteger, UIImageResizingMode) {
    UIImageResizingModeTile,
    UIImageResizingModeStretch,
};

位掩码

当用到位掩码时,使用 NS_OPTIONS 宏。

举例:

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

布尔

typedef signed char BOOL;
#define YES ((BOOL)1)
#define NO  ((BOOL)0)

以上代码片断来自objc.h。永远不将布尔变量直接和 YES 进行比较,因为 YES 被定义为 1,而 BOOL 可以多达 8 位。

if (isAwesome)
if (![someObject boolValue])

反对:

if ([someObject boolValue] == NO)
if (isAwesome == YES) 

单例

单例对象应该使用线程安全的模式创建共享的实例。

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

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

   return sharedInstance;
}

其他 Objective-C 风格指南

如果感觉我们的不太符合你的口味,可以看看这两个风格指南:Google Objective-C Style GuideNYTimes Objective-C Style Guide,本指南也从它们那里获得了很多直接的帮助和间接的启发。

上一篇 下一篇

猜你喜欢

热点阅读