写代码最重要的是实现功能,但是除了实现功能之外,我们还应该想办法,让代码变得更规范,更漂亮
最近在读《禅与Objective-C编程艺术》和《Effective Objective C 2.0:编写高质量iOS与OS X代码的52个有效方法》,这两本都讲解了代码规范方面的东西,结合自己平时的代码习惯,发现有很多地方自己做的还是不够好,代码写得不够帅,所以总结一下,让以后的代码更帅一点
条件语句
条件语句一定要使用括号,如果不使用括号,if后面的那行代码删除,之后的代码会成为if语句里面的代码
推荐:
if (!error) {
return success;
}
括号
大括号的使用最好跟Apple保持一致,大括号在同一行开始,在新的一行结束,
如:
if (user.isHappy) {
//Do something
}
else {
//Do something else
}
nil和BOOL判断
最好直接使用!
判断nil
,或者BOOL值
推荐:
if (![someObject boolValue]) { ...
if (!someObject) { ...
避免嵌套if
不要嵌套 if 语句。使用多个 return 可以避免增加循环的复杂度,并提高代码的可读性。
推荐:
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
//Do something important
}
不推荐:
- (void)someMethod {
if ([someOther boolValue]) {
//Do something important
}
}
多用字面量语法
何为字面量语法
NSString *string = @"string";
NSNumber *intNumber = @88;
NSNumber *boolNumber = @YES;
NSNumber *floatNumber = @3.14;
int var = 3;
NSNumber *varNumber = @(var);
NSArray *list = @[@"itme1",@"item2",@"item3"];
NSLog(@"%@,%@,%@",list[0],list[1],list[2]);
NSDictionary *map = @{@"key1":@"value1",@"key2":@"value2"};
NSLog(@"%@,%@",map[@"key1"],map[@"key2"]);
这就是字面量语法,用非常简单直观的方法创建或者获取常用的对象(NSString,NSNumber,NSArray,NSDictionary),使用字面量语法简单方便直观
复杂的表达式
当有一个复杂的if语句时,可以把判断条件提取出来作为BOOL变量
BOOL nameContainsSwift = [sessionName containsString:@"Swift"];
BOOL isCurrentYear = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession = nameContainsSwift && isCurrentYear;
if (isSwiftSession) {
// Do something very cool
}
用常量代替宏
推荐:
static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const NSTimeInterval animationDuration = 3.0;
不推荐:
#define ZOCCacheControllerDidClearCacheNotification @"ZOCCacheControllerDidClearCacheNotification"
#define animationDuration 3.0
原因:
1. 使用宏的过程中无法直接知道常量类型(是int呢?还是Stiring呢?),除非查看宏定义的地方
2. 宏可以更改,,如果2个地方使用同样的名字定义了2个不一样的宏,你使用的时候可能会使用错误,造成难以发现的bug。使用const定义的常量如果更改编译器就会报错
static
在常量的使用过程中分两种情况
1.常量需要对外暴露。不使用static
,并在.h中用extern
暴露出去
// .h
extern NSString *const ZOCCacheControllerDidClearCacheNotification;
// .m
NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
2.常量只在本类中使用,不需要对外暴露,使用static
.
static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static
定义的变量仅在当前的.m文件中可见,如果常量只在本类中使用,用static
会避免影响到其他类,如果需要全局可见,就不能使用static
类
命名冲突
1)类名,全局变量和C函数(C函数类似于全局变量)命名时,使用三个字母作为前缀,避免命名跟第三方API或者苹果API名称冲突
项目里经常看到不使用前缀或者使用两个字母作为前缀的命名方式。
不使用前缀可能发生冲突不言而喻,那为什么不使用两个字母作为前缀呢?
因为Apple宣称其保留使用所有”两字母前缀”的权利,使用两字母作为前缀有跟未来的苹果API冲突的风险,所以保险起见,尽量使用三个字母作为iOS的命名前缀
2)类内部使用的私有方法,可以加个前缀,但不要使用单个下划线_
作为前缀,因为这也是Apple预留的。可以使用双下划线__
或者p_
作为前缀
3)如果需要为无源码的类(包括第三方类和系统类)添加category,方法名称前最好带前缀,避免冲突
Designated Initializer
子类重新定义designated initializer时应遵循以下步骤:
- 定义designated initializer,并调用父类的designated initializer
- 重载父类的designated initializer,并调用新定义的designated initializer
- 为新的designated initializer写文档
在.h中,使用__attribute__((objc_designated_initializer))
标明哪个是designated initializer。
如果因为某些原因,父类的designated initializer被弃用,比如designated initializer必须要有某个参数,父类的designated initializer无法传这个参数。用__attribute__((unavailable("Invoke the designated initializer")))
表示弃用
- (instancetype)initWithName:(NSString *)name __attribute__((objc_designated_initializer));
- (instancetype)init __attribute__((unavailable("Invoke -initWithName:")));
类的封装
在封装一个类的时候,下面几点需要注意:
- 外部不需要了解的属性和方法,尽量定义在.m中
- 不要轻易暴露属性的setter方法,多使用readonly
- 尽量使用不可变对象
- 不要暴露可变的collection,应该提供相应的方法替代
protocol
1)一般情况下protocol分为2类,delegate和dataSource
- delegate:委托别的类去干活的,需要放在delegate里面,一般没有返回值
- dataSource:去别的类获取数据的,放在dataSource里面,主要用于获取数据
2)delegate属性在ARC下,一定要用weak取代assign,避免隐患
3)对于option的protocol。在调用之前一定要用-respondsToSelector:
判断这个方法是否实现了
属性
规范
从代码规范和漂亮的角度来说,属性最好这样写,这是apple的写法
推荐:
@property(nonatomic, readonly, copy) NSString *nibName;
不推荐:
@property(nonatomic, readonly, copy) NSString* nibName;
@property(nonatomic, readonly, copy)NSString *nibName;
@property (nonatomic, readonly, copy) NSString *nibName;
...
setter&getter
属性除了init和dealloc外,建议使用setter和getter方法。在init和dealloc中,建议直接使用ivar
使用setter和getter方法的好处:
- 遵守内存管理语义(strong,copy…)
- kvo通知会自动被执行
- 方便debug,打断点
- 方便重写getter或者setter
为什么不要在init和dealloc中使用setter和getter?
如果init中使用了setter,在子类的属性初始化之前,调用[super init]的时候就会调用setter方法。这时候如果重写了setter,并在setter中做了特殊的操作,这时候就可能会引起一些非常难发现的bug。所以为了避免隐患,在init或者dealloc中直接使用ivar
可变对象赋值
对可以用可变对象赋值的属性(如:NSString,NSArray,NSDictionary),属性的内存管理类型必须为copy
。这是为了防止可变对象(NSMultableArray)给不可变对象(NSArray)赋值。导致不可变对象(NSArray)指针指向可变对象(NSMultableArray),在使用过程中可能会改变它的值,出现bug
点符号
当使用属性的时候尽量使用.
符号,方法调用使用[]
例:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
Block
推荐这种block使用方法
__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomethingWithData:data];
[strongSelf doSomethingWithData:data];
}
}];
在block外面用weak持有self,避免循环引用
在block里面用strong持有self,避免多线程中block执行到一半,self被释放为nil,而出现的隐患
其他的block问题,可以看以前写的这篇文章
可变集合
永远不要枚举可变集合
推荐:
NSArray *staticArray = [multableArray copy];
for (id item in staticArray) {
....
}
不推荐:
for (id item in multableArray) {
....
}
可变集合在枚举时如果发生改变,会引起crash。
永远不要枚举可变集合,不管你有多么确定这个集合不会改变。因为代码也许以后会被修改,也许有别的线程会改变这个集合,太多意外可能会发生,唯一能保证不会有意外的就是永远不要枚举可变集合
不要将可变集合暴露成公共属性
推荐:
// .h
@property(nonatomic, readonly) NSArray *staticArray;
// .m
@property(nonatomic, strong) NSMutableArray *mutableArray;
- (NSArray *)staticArray
{
return [self.mutableArray copy];
}
不推荐①:
// .h
@property(nonatomic, readonly)NSMutableArray *mutableArray;
不推荐②:
// .h
@property(nonatomic, readonly) NSArray *staticArray;
// .m
@property(nonatomic, strong) NSMutableArray *mutableArray;
- (NSArray *)staticArray
{
return self.mutableArray;
}
你永远不知道外面会怎么用这个mutableArray,假设外面正在枚举这个mutableArray,在其他线程mutableArray被改变了。嘭,Crash了,还不是毕现,又是加班的节奏了…
如果在外部需要改变这个Array里面的item,添加改变item的方法:
// .h
@property(nonatomic, readonly) NSArray *staticArray;
- (void)addItem:(id)item;
- (void)removeItem:(id)item;
//.m
@property(nonatomic, strong) NSMutableArray *mutableArray;
- (void)addItem:(id)item
{
[self.mutableArray addObject:item];
}
- (void)removeItem:(id)item
{
[self.mutableArray removeObject:item];
}
NSNotification
remove
notification一定要remove!
notification一定要remove!!
notification一定要remove!!!
重要的东西一定要说三遍,Notification不remove很容易crash,包括KVO,也一定要记得remove,否则会有crash在前方等着你…
多次注册
notification如果多次注册会导致一次post,方法多次被调用,所以注意注册通知的时候一定要看清楚,是否通知只注册了一次,建议在init中注册通知,dealloc中remove通知
Reference
Effective Objective C 2.0:编写高质量iOS与OS X代码的52个有效方法
版权声明:本文为博主原创文章,未经博主允许不得转载。