方法声明与定义:
1.星号前统一要求需要输入空格
2.方法体左大括号于方法名的右侧
3.-或者+与返回类型之间需要有空格。参数列表中,只有参数之间有空格。
- (void)doSomethingWithString:(NSString *)theString {
...
}
如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。
- (void)doSomethingWith:(GTMFoo *)theFoo
rect:(NSRect)theRect
interval:(float)theInterval {
...
}
协议:
尖括号所包括的协议名称与前面的类型标识之间不应该有空格
@interface MyProtocoledClass : NSObject {
@private id delegate;
}
方法名:
方法名应该读起来就像句子,这表示你应该选择与方法名连在一起读起来通顺的参数名。
convertPoint:fromRect:
replaceCharactersInRange:withString:
访问器方法应该与他们getting的的成员变量的名字一样,但不应该以get作为前缀。
例如:
- (id)getDelegate; // AVOID
- (id)delegate; // GOOD
变量名:
要为变量起一个描述性的名字。不要担心浪费列宽,若包含两个以上单词的命名应该使用驼峰命名方式,因为让新的代码阅读者立即理解你的代码更重要。
错误的命名:
int w;
int nerr;
int nCompConns;
UIButton*settingsButton;
tix = [[NSMutableArray alloc] init];
obj = [someObject object];
p = [network port];
正确的命名:
int numErrors;
int numCompletedConnections;
tickets = [[NSMutableArray alloc] init];
userInfo = [someObject object];
port = [network port];
星号应紧贴变量名称表示指向变量的指针,比如:
正确用法:
NSString *text;
不当用法:
NSString* text;
NSString * text;
方法调用:
方法调用时,所有参数应该在同一行
[myObject doFooWith:arg1 name:arg2 error:arg3];
或者每行一个参数,以冒号对齐
[myObject doFooWith:arg1
name:arg2
error:arg3];
[UIViewanimateWithDuration:1.0
animations:^{
// something
}
completion:^(BOOL finished) {
// something
}];
异常:
每个@标签应该有独立的一行,在@与{}之间需要有一个空格。@catch与被捕捉到的异常对象的声明之间也要有一个空格。
@try {
foo();
}
@catch (NSException *ex) {
bar(ex);
}
@finally {
baz();
}
条件语句的编写:
1.为避免错误,条件语句体必须使用大括号,即便语句体中的语句可以不必使用大括号(比如只有一行语句)。常见的错误包括在不使用大括号的情况下添加第二行语句,以为它属于if语句的一部分。此外,更可怕的事情是,如果条件语句中的代码行被注释,则本不术语条件语句的下一行代码将变成条件语句的一部分。此外,这种编码风格和所有其它条件语句均保持一致.
例如:
恰当用法:
if(!error) {
returnsuccess;
}
不当用法:
if(!error)
returnsuccess;
不当用法2:
if(!error)returnsuccess;
2. 编写括号内的判断语句时,为避免手误演变成赋值操作,一般将常量或nil放置在左边
例如:
if(nil == self.dataList) {
}
if(0 == theCount) {
}
Case语句
除非编译器强制要求,括号在 case 语句里面是不必要的。但是当一个 case 包含了多行语句的时候,需要加上括号。
switch (condition) {
case 1:
// ...
break;
case 2: {
// ...
// Multi-line example using braces
break;
}
case 3:
// ...
break;
default:
// ...
break;
}
有时候可以使用 fall-through 在不同的 case 里面执行同一段代码。一个 fall-through 是指移除 case 语句的 “break” 然后让下面的 case 继续执行。
switch (condition) {
case 1:
case 2:
// code executed for values 1 and 2
break;
default:
// ...
break;
}
常量
相对字符串字面量或数字,我们更推荐适用常量。应使用static方式声明常量,而非使用#define的方式来定义宏。
例如:
恰当用法:
static NSString * const NYTAboutViewControllerCompanyName =@"The New York Times Company";
static const CGFloat NYTImageThumbnailHeight =50.0;
不当用法:
#define CompanyName @"The New York Times Company"
#define thumbnailHeight 2
枚举类型
在使用enum的时候,推荐适用最新的fixed underlying type(WWDC 2012 session 405- Modern Objective-C)规范,因为它具备更强的类型检查和代码完成功。
例如:
typedefNS_ENUM(NSInteger, NYTAdRequestState) {
NYTAdRequestStateInactive,
NYTAdRequestStateLoading
};
布尔变量
因为nil将被解析为NO,因此没有必要在条件语句中进行比较。永远不要将任何东西和YES进行直接比较,因为YES被定义为1,而一个BOOL变量可以有8个字节。
例如:
恰当用法:
if(!someObject) {
}
不当用法:
if(someObject==nil) {
}
以下是BOOL变量的使用:
恰当用法:
if(isAwesome)
if(![someObject boolValue])
不当用法:
if([someObject boolValue]==NO)
if(isAwesome==YES)// Never do this.
三目运算
仅当使用该运算子可以让代码显得更清晰易懂时方可使用三元运算子,切三目运算体内应均为运算结果,不然被忽略的错误比较容易产生。 更多情况下应使用条件语句。使用类似if的条件语句对多种条件进行判断通常要更容易理解,或使用实例变量。
恰当用法:
result=a>b?x:y;
不当用法:
result=a>b?x=c>d?c:d:y;
CGRect函数
当需要获取一个CGRect矩形的x,y,width,height属性时,应使用CGGeometry函数,而非直接访问结构体成员。
例如:
恰当用法:
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;
单例
在创建单例对象的共享实例时,应使用线程安全模式,获取单例对象类方法命名使用sharedXXXX。
例如:
(instancetype)sharedInstance{
staticidsharedInstance =nil;
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
美化代码:
空格
方法的大括号和其他的大括号(if/else/switch/while 等) 总是在同一行开始,在新起一行结束。
推荐:
if (user.isHappy) {
//Do something
}
else {
//Do something else
}
不推荐:
if (user.isHappy)
{
//Do something
} else {
//Do something else
}
换行
self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
一个像上面的长行的代码在第二行以一个间隔(2个空格)延续
self.productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productIdentifiers];
利用代码块
一个 GCC 非常模糊的特性,以及 Clang 也有的特性是,代码块如果在闭合的圆括号内的话,会返回最后语句的值
NSURL *url = ({
NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint];
[NSURL URLWithString:urlString];
});
所有的变量都在代码块中,也就是只在代码块的区域中有效,这意味着可以减少对其他作用域的命名污染。
Pragma Mark
#pragma mark - 是一个在类内部组织代码并且帮助你分组方法实现的好办法。 我们建议使用 #pragma mark - 来分离:
#pragma mark - View Lifecycle (View 的生命周期)
- (void)viewDidLoad { /* ... */ }
- (void)viewWillAppear:(BOOL)animated { /* ... */ }
- (void)didReceiveMemoryWarning { /* ... */ }
#pragma mark - Custom Accessors (自定义访问器)
- (void)setCustomProperty:(id)value { /* ... */ }
- (id)customProperty { /* ... */ }
建立委托者和数据源使用“weak关键字”
@property (nonatomic, weak) id delegate;
@property (nonatomic, weak) id dataSource;
默认情况下,委托对象需要实现 protocol 的方法。可以用@required 和 @optional 关键字来标记方法是否是必要的还是可选的。
@protocol ZOCSignUpViewControllerDelegate
@required
- (void)signUpViewController:(ZOCSignUpViewController *)controller didProvideSignUpInfo:(NSDictionary *);
@optional
- (void)signUpViewControllerDidPressSignUpButton:(ZOCSignUpViewController *)controller;
@end
对于可选的方法,委托者必须在发送消息前检查委托是否确实实现了特定的方法(否则会 crash):
if ([self.delegate respondsToSelector:@selector(signUpViewControllerDidPressSignUpButton:)]) {
[self.delegate signUpViewControllerDidPressSignUpButton:self];
}