<<禅与Objective-C编程艺术>>
一.条件语句
1.条件语句体应该总是被大括号包围来避免错误,即使可以不用。
推荐:
if(!error){
return success;
}
不推荐:
if(!error)
return success;
不推荐:
if(!error) return success;
2.不要使用尤他表达式。(尤他表达式是指拿一个常量去和变量比较而不是拿变量去和常量比较)
推荐:
if([myValue isEqual:@42]){
...
}
不推荐:
if([@42 isEqual:myValue]){
...
}
推荐:
if(someObject){...
if(![someObject boolValue]){...
if(!someObject){...
不推荐:
if(someObject == YES) { ... //Wrong
if(myRawValue == YES){... // Never do this
if([someObject boolValue] == NO) { ...
3.黄金大道:
“当编写条件语句的时候,左边的代码间距应该是一个“黄金”或者“快乐”的大道。 这是说,不要嵌套if语句。多个 return 语句是OK的。”
推荐:
-(void)someMethod {
if(![someOther boolValue]){
return;
}
// Do something import
}
不推荐:
-(void)someMethod {
if([someOther boolValue]) {
// Do something important
}
}
4.复杂表达式:当有一个复杂的if子句的时候,应该把它们提取出来赋给一个BOOL变量,这样可以让逻辑更清楚,而且让每个子句的意义体现出来。
BOOL nameContainsSwift = [sessionName containsString:@"swift"];
BOOL isCurrentYear = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession = nameContainsSwift && isCurrentYear;
if(isSwiftSession) {
// Do something very cool
}
5.三元运算符:应该只用在它能让代码更加清楚的地方。一个条件语句的所有的变量应该是已经被求值的。计算多个条件子句通常会让语句更加难以理解。
推荐:
result = a > b ? x : y;
不推荐:
result = a > b ? x = c > d ? c : d : y;
二. case语句
1.除非编译器强制要求,括号在case语句里是不必要的。但是当一个case包含了多行语句的时候,需要加上括号。
2.当在switch语句里面使用一个可枚举变量的时候,default是不必要的。比如:
switch(menuType){
case ZOCEnumNone:
//...
break;
case ZOCEnumValue1:
//...
break;
case ZOCEnumValue2:
//...
break;
}
3.Enumarated Types 枚举类型
当使用enum的时候,建议使用新的固定的基础类型定义,因为它有更强大的类型检查和代码补全。
typedef NS_ENUM(NSUInteger,ZOCMachineState){
ZOCMachineStateNone,
ZOCMachineStateIdle,
ZOCMachineStateRunning,
ZOCMachineStatePaused,
};
三.命名
1.通用的约定:推荐使用长的,描述性的方法和变量名
推荐:
UIButton *settingsButton;
不推荐:
UIButton *setBut;
2.常量:
常量应该使用驼峰命名法,应该用相关的类名作为前缀。
//推荐:
static const NSTimeInterval ZOCSignInViewFadeOutAnimationDuration = 0.4;
//不推荐:
static const NSTimeInterval fadeOutTime = 0.4
常量应该尽量使用in-line 的字符串字面值或者数字,这样便于经常用到的时候复用,并且可以快速修改而不用查找和替换。常量应该用static声明,并且不要使用#define,除非它就是明确作为一个宏来用的。
//推荐
static NSString *const ZOCatchControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const CGFloat ZOCImageThumbnailHeight = 50.0f;
//不推荐
#define CompanyName @"Apple Inc."
#define magicNumber 42
常量应该在interface 文件中这样被声明:
extern NSString *const ZOCatchControllerDidClearCacheNotification;
并且应该在实现文件中实现它的定义。
3.方法
对于方法签名,在方法类型(-/+ 符号)后应该要有一个空格。方法段之间也应该有一个空格。在参数名称之前总是应该有一个描述性的关键字。
(“and”不应该用作阐明有多个参数)
//推荐:
- (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;
//不推荐:
- (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.
4.字面值
NSString, NSDictionary, NSArray 和 NSNumber 字面值应该用在任何创建不可变的实例对象。(nil不能放进NSArray 和 NSDictionary里,这会导致 Crash。)
例子:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
对于那些可变的副本,推荐使用明确的如NSMutableArray, NSMutableString这些类。
四.类
1.类名:类名应该加上三个大写字母作为前缀(减少objective-c没有命名空间所带来的问题)。建议在定义Core Data 对象时严格遵循这个约定,因为你最后可能把你的 Managed Object Model 和其他(第三方库) 的Managed Object Model 合并。
当你创建一个子类的时候,应该把说明性的部分放在前缀和父类名的中间。如果你有一个ZOCNetworkClient类,子类的名字会是ZOCTwitterNetworkClient。
2.Initializer 和 dealloc 初始化
推荐的代码组织方式:将 dealloc 方法放在实现文件的最前面(直接在@synthesize以及 @dynamic 之后),init 应该放在 dealloc 之后。
init 方法结构:
-(instancetype)init
{
self = [super init];
if(self){
//Custom initialization
}
return self;
}
~alloc表示对象分配内存,这个过程涉及分配足够的可用内存来保存对象,写入 isa 指针,初始化 retain 的计数,并且初始化所有的实例变量。
~init 是表示初始化对象,这意味着把对象放到了一个可用的状态。这通常是指把对象的实例变量赋给了可用的值。
- instancetype: 在 alloc 或者 init 中,强烈建议对所有返回类的实例的类方法和实例方法使用 instancetype 类型。
4.如果可能,请尽量避免使用单例而是依赖注入。如果一定要使用,请使用一个线程安全的模式来创建共享的实例。对于GCD,用dispatch_once()函数(把一些东西执行一次)就可以。
5.属性应该尽可能描述性地命名,避免缩写,并且是小写字母开头的驼峰命名。
应该总是使用setter和getter方法访问属性,除了init 和 dealloc方法。因为:
a. 使用setter会遵守定义的内存管理语义(strong,weak,copy...)
b. KVO通知会被自动执行
c. 更容易debug
d. 允许在一个单独的地方为设置值添加额外的逻辑
6.Init 和 Dealloc
永远不能在init(以及其他初始化函数)里面用getter 和 setter 方法,而是直接访问实例变量。
使用setter getter 方法的时候尽量使用点语法。