格式

1、任意函数长度不得超过50行。
2、任意行代码不得超过80字符。可以在设置中设置超过80个字符的提醒。

3、在定义函数的行前留白一行。
4、功能相近的代码要放在一起。
5、使用#pragma来切分不同功能区域的代码。
6、二元运算符和参数之间需要放置一个空格,一元运算符、强制类型转换和参数之间不放置空格。关键字之后圆括号之前需要放置一个空格。

1
2
3
4
5
void *ptr = &value + 10 * 3;
NSString *str1 = (NSString *)str2;
for (int i = 0; i < 10; i++) {
[self doSomeThing];
}

7、长的字面值应被拆分为多行。

1
2
3
4
5
6
7
8
9
10
11
12
13
NSArray *theShit = @[
@"Got some long string objects in here.",
[AndSomeModelObjects too],
@"Moar strings."
];

NSDictionary *keyedShit = @{
@"this.key": @"corresponds to this value",
@"otherKey": @"remoteData.payload",
@"some": @"more",
@"JSON": @"keys",
@"and": @"stuff",
};

命名

命名是编程中最基本的技能,我们给变量、函数、类、包等等命名。给他们以名字,让他们有意义,既能表示他们到底是做什么的,也能将其与其他变量区别开来。像人一样娶一个好名字至关重要,“丁当”总比“狗蛋”来的好听。 为什么要命名? 命名一定要“名副其实”,尽可能使用有意的名称,而且这个意义和指称的变量真实意义相关。
1、基本原则
尽量不要出现没有任何意义的命名类似于下述形式的命名:

1
2
3
int a = 1;
int b = 3;
CGPoint point = CGPointMake(a,b);

如果换成下面的形式是不是可读性强了很多:

1
2
3
int startX = 1;
int startY = 3;
CGPoint startPoint = CGPointMake(startX,startY);

命名首字母大写,其他命名首字母小写。并且采用驼峰格式分割单词。 例如:FXTest
仿照 Cocoa 风格来,使用长命名风格。
变量命名推荐的命名语素顺序是:最开头是命名空间简写,然后越重要、区别度越大的语素越要往前放。经典的结构是:作用范围+限定修饰+类型。

1
2
3
4
5
extern ushort APIDefaultPageSize;        // 还行,能明白意思了
extern ushort APIDefaultFetchPageSize; // 加上些限定更好一些
extern ushort APIFetchPageSizeDefault; // 再好些,把重要的往前放
FXToolbarComment // 不推荐
FXCommentToolbar // OK,把类型(toolbar)置后

2、类别的扩展名以“被扩展的类名+自定义命名部分组成” 例如:

1
NSSstring+Utils.h

3、通知命名
基本命名格式是:与通知相关的类名 + Did | Will + UniquePartOfName + Notification,例:

1
2
3
4
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification

4、宏定义,全部字母大写,例如: #define BW_DEBUG 1
5、常量定义,字符串定义以小写字母k开头,随后首字母大写,例如:

1
static NSString* const kBWBarTitle = @"动态";

ps:如果要定义常量使用static const优于宏定义,前者会进行类型检查。
6、缩略词,以下是一些常用的首字母缩略词:

1
ASCII,PDF,XML,HTML,URL,RTF,HTTP,TIFF,JPG,PNG,GIF,LZW,ROM,RGB,CMYK,MIDI,FTP

7、命名空间,因为OC没有命名空间的概念,所以使用前两个或者多个字母来表示命名空间,例如"NSObject中的NS",我们也使用自己的命名空间。比如:

1
2
发现模块:DiscoverController,模块里面其他的控件或者model,DCModel,DCView
个人中心模块:PersonalCenterViewController,模块里面其他的控件或者model,PCModel,PCView

点标记语法

推荐:

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

不推荐:

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

方法命名

1、方法命名尽可能清晰
推荐:

1
2
3
insertObject:atIndex:
removeObjectAtIndex:
removeObject:

不推荐:

1
2
insert:at:
remove:

2、类方法声明在方法类型与返回类型之间要有空格。
推荐:

1
- (void)methodName:(NSString *)string;

不推荐:

1
-(void)methodName:(NSString *)string;

3、函数
(1)、调用时所有参数应该在同一行

1
[myObject doFooWith:arg1 name:arg2 error:arg3];

(2)、或者每行一个参数,以冒号对齐:

1
2
3
[myObject doFooWith:arg1
name:arg2
error:arg3];

对于参数过多的函数,尽量使用后面一种对其方式。
(3)、不要使用下面的缩进风格:

1
2
3
4
5
6
7
[myObject doFooWith:arg1 name:arg2  // some lines with >1 arg
error:arg3];
[myObject doFooWith:arg1
name:arg2 error:arg3];
[myObject doFooWith:arg1
name:arg2 // aligning keywords instead of colons
error:arg3];

(4)、如果对传入参数进行数据保护尽量不要用if(!objc),使用断言来处理。

1
2
3
4
- (void)sendArgs:(NSDictionary*)args {
NSAssert(args, @"args is nil");
.....
}

(5)、方法参数名前一般使用的前缀包括“the”、“an”、“new”。示例:

1
2
3
4
5
- (void)setTitle:(NSString *)aTitle;
- (void)setName:(NSString *)newName;
- (id)keyForOption:(CDCOption *)anOption
- (NSArray *)emailsForMailbox:(CDCMailbox *)theMailbox;
- (CDCEmail *)emailForRecipients: (NSArray *)theRecipients;

4、方法名
alloccopyinitmutableCopynew 开头的方法要注意,它们会改变ARC的行为。
getset 开头的方法有特殊的意义,不要随意定义。

  • set 是属性默认的设置方法,如果函数不是为了设置类成员,则不要用 set 开头,可用 setup 替代。
  • get 和属性方法无关,但在 Cocoa 中,其标准行为是通过引用传值,而不是直接返回结果的。欲获取变量,直接以变量名为名,如:userInfomation,而不是 getUserInfomation
    例:
    推荐:
    1
    2
    3
    4
    5
    - (NSString *)name;
    - (void)getName:(NSString **)buffer range:(NSRange)inRange;
    - (NSSize)cellSize;
    - (void)setupControllerObservers;
    - (void)setupController;
    不推荐:
    1
    2
    3
    - (NSString *)getName;
    - (NSSize)calcCellSize;
    - (void)setController;

    三元运算符

    长的三元运算符应使用圆括号括起来。三元运算符仅用于赋值和做参数。
    1
    NSString *gender = (stuff == thing ? @"男" : @"女");
    当有nil时的三元运算符
    1
    NSString *name = thingThatCouldBeNil ? defaultValue : @"";
    不推荐
    1
    NSString *name = thingThatCouldBeNil ?: defaultValue;

    枚举类型

    当使用enum关键字时,推荐使用苹果最新引入的固定基础类型语法,因为这将获得强类型检查与代码完成功能。SDK现在包含了一个固定基础类型的宏—NS_ENUM()
    1
    2
    3
    4
    5
    6
    7
    typedef NS_ENUM(NSInteger, CertifiedModifyPassWordType)
    {
    CertifiedModifyPassWordTypeWithdrawals = 1, // 提现
    CertifiedModifyPassWordTypePaymentFail, // 支付失败
    CertifiedModifyPassWordTypePayment, // 支付
    CertifiedModifyPassWordTypeOther
    };

    布尔类型

    因为nil被解析为了NO,所以和nil作比较没有任何的必要。不要将变量和YES直接比较,因为YES被定义为1而BOOL类型是8位的unsigned int,即BOOL的值不仅仅是1或0。
    推荐:
    1
    2
    if (!someObject) {
    }
    不推荐:
    1
    2
    if (someObject == nil) {
    }
    推荐:
    1
    2
    if (isAwesome)
    if (![someObject boolValue])
    不推荐:
    1
    2
    if ([someObject boolValue] == NO)
    if (isAwesome == YES) // Never do this.

    单例

    应该使用线程安全的模式创建共享的单例实例。
    1
    2
    3
    4
    5
    6
    7
    8
    + (instancetype)sharedInstance {
    static id sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
    }
    单例的另一种做法,利用+ initialize方法。
    1
    2
    3
    4
    5
    6
    7
    static JSONAPI* sharedInstance = nil;
    + (void)initialize {
    static dispatch_once_t once;
    dispatch_once(&once, ^{
    sharedInstance = [[JSONAPI alloc] init];
    });
    }

    Block相关

    在block中使用到self变量的时候,一定要先weak再strong。
    1
    2
    3
    4
    5
    6
    7
    __weak typeof(self) weakSelf = self;
    [self doABlockOperation:^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
    ...
    }
    }];

    Delegate

    代理协议名,常用delegate、dateSource做结尾。
    1
    2
    - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
    - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
    didwillshould等形容词,代表Event事件的Delegate
    1
    2
    3
    - (BOOL)windowShouldClose:(id)sender;
    - (void)browserDidScroll:(NSBrowser *)sender;
    - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;

    控制结构,if-else

    1、方法的花括号推荐另起一行。方法内部需要写在一行。
    1
    2
    3
    4
    5
    - (void)methodName:(NSString *)string {
    if () {
    } else {
    }
    }
    2、条件判断的括号内侧不应有空格。
    推荐:
    1
    2
    3
    if (a < b) {
    // something
    }
    不推荐:
    1
    2
    3
    if ( a < b ) {
    // something
    }
    3、关系运算符(如>=!=)和逻辑运算符(如&&||)两边要有空格。
    1
    (someValue > 100) ? YES : NO
    二元算数运算符两侧是否加空格不确定,根据情况自己定。一元运算符与操作数之前没有空格。
    多个参数逗号后留一个空格(这也符合正常的西文语法)。
    4、当需要满足一定条件时才执行某项操作时,使用return是正常合理的。
    推荐:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (void)someMethod {
    if (![someOther boolValue]) {
    return;
    }
    //Do something important
    }

    if (!error) {
    return success;
    }
    不推荐:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (void)someMethod {
    if ([someOther boolValue]) {
    //Do something important
    }
    }

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

    UIKit

    UIView的子类初始化的时候,不要进行任何布局操作。布局操作在LayoutSubViews里面做。
    UIView的子类布局必须在layoutSubViews里面进行,需要布局的时候调用setNeedLayout来告诉系统,需要重新布局该View,不要直接调用layoutSubViews。

    @class

    在类定义中使用到自己定义的类的时候,尽量不要在头文件中引入自己定义的类的同文件,使用@class替换。在实现文件中引入相应头文件。例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // BWTest.h
    @class BWDataCenter;
    @interface BWTest : NSObject
    @property (nonatomic, strong) BWDataCenter* dataCenter;
    @end

    // BWTest.m
    #import "BWDataCenter.h"
    @implementation BWTest
    @end

    项目工程文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #pragma mark - init Method
    填入init,initWithFrame等方法
    #pragma mark- View Life Cycle
    填入viewdidload,viewdidappear等方法
    #pragma mark- Delegate,DataSource, Callback Method
    填入tableview,scrollview等代理方法
    #pragma mark- Override Parent Methods
    填入updateViewConstraints,updateConstraint, prepareForSegue等方法
    #pragma mark- SubViews Configuration
    填入configureSubViews,configureTableView等方法,这里的方法在init方法或view life cycle被调用
    #pragma mark- Actions
    填入-(IBAction)action:(id)sender和[self addtarget:self action:@selector(action:)]动作指向的方法
    #pragma mark- Public Methods
    填入在.h外暴露的方法
    #pragma mark- Private Methods
    填入.m文件内部调用的方法
    #pragma mark- Getter Setter
    填入对@property初始化的方法