尽管很多时候能用代替alloc init方法,但这可能会导致调试内存时出现不可预料的问题。Cocoa的规范就是使用alloc init方法,使用new会让一些读者困惑。

    Public API要尽量简洁

    共有接口要设计的简洁,满足核心的功能需求就可以了。不要设计很少会被用到,但是参数极其复杂的API。如果要定义复杂的方法,使用类别或者类扩展。

    #import和#include

    #import是Cocoa中常用的引用头文件的方式,它能自动防止重复引用文件,什么时候使用#import,什么时候使用#include呢?

    • 当引用的是一个Objective-C或者Objective-C++的头文件时,使用#import
    • 当引用的是一个C或者C++的头文件时,使用#include,这时必须要保证被引用的文件提供了保护域(#define guard)。

    栗子:

    为什么不全部使用#import呢?主要是为了保证代码在不同平台间共享时不出现问题。

    引用框架的根头文件

    1. //正确,引用根头文件
    2. #import <Foundation/Foundation.h>
    3. //错误,不要单独引用框架内的其它头文件
    4. #import <Foundation/NSArray.h>
    5. #import <Foundation/NSString.h>

    BOOL在Objective-C中被定义为signed char类型,这意味着一个BOOL类型的变量不仅仅可以表示YES(1)和NO(0)两个值,所以永远不要将BOOL类型变量直接和YES比较:

    1. //错误,无法确定|great|的值是否是YES(1),不要将BOOL值直接与YES比较
    2. BOOL great = [foo isGreat];
    3. // ...be great!
    4. //正确
    5. BOOL great = [foo isGreat];
    6. if (great)

    同样的,也不要将其它类型的值作为BOOL来返回,这种情况下,BOOL变量只会取值的最后一个字节来赋值,这样很可能会取到0(NO)。但是,一些逻辑操作符比如&&,||,!的返回是可以直接赋给BOOL的:

    另外BOOL类型可以和_Bool,bool相互转化,但是不能Boolean转化。

    使用ARC

    除非想要兼容一些古董级的机器和操作系统,我们没有理由放弃使用ARC。在最新版的Xcode(6.2)中,ARC是自动打开的,所以直接使用就好了。

    在init和dealloc中不要用存取方法访问实例变量

    init``dealloc方法被执行时,类的运行时环境不是处于正常状态的,使用存取方法访问变量可能会导致不可预料的结果,因此应当在这两个方法内直接访问实例变量。

    1. //正确,直接访问实例变量
    2. - (instancetype)init {
    3. self = [super init];
    4. if (self) {
    5. _bar = [[NSMutableString alloc] init];
    6. }
    7. return self;
    8. }
    9. - (void)dealloc {
    10. [_bar release];
    11. [super dealloc];
    12. }
    13. //错误,不要通过存取方法访问
    14. - (instancetype)init {
    15. self = [super init];
    16. if (self) {
    17. self.bar = [NSMutableString string];
    18. return self;
    19. - (void)dealloc {
    20. self.bar = nil;
    21. [super dealloc];
    22. }

    按照定义的顺序释放资源

    NSString非常常用,在它被传递或者赋值时应当保证是以复制(copy)的方式进行的,这样可以防止在不知情的情况下String的值被其它对象修改。

    1. - (void)setFoo:(NSString *)aFoo {
    2. _foo = [aFoo copy];
    3. }

    使用NSNumber的语法糖

    使用带有@符号的语法糖来生成NSNumber对象能使代码更简洁:

    nil检查

    因为在Objective-C中向nil对象发送命令是不会抛出异常或者导致崩溃的,只是完全的“什么都不干”,所以,只在程序中使用nil来做逻辑上的检查。

    另外,不要使用诸如nil == Object或者Object == nil的形式来判断。

    1. //正确,直接判断
    2. if (!objc) {
    3. ...
    4. }
    5. //错误,不要使用nil == Object的形式
    6. if (nil == objc) {
    7. ...
    8. }

    属性的线程安全

    定义一个属性时,编译器会自动生成线程安全的存取方法(Atomic),但这样会大大降低性能,特别是对于那些需要频繁存取的属性来说,是极大的浪费。所以如果定义的属性不需要线程保护,记得手动添加属性关键字nonatomic来取消编译器的优化。

    1. //正确,使用点分语法访问属性
    2. NSString *oldName = myObject.name;
    3. myObject.name = @"Alice";
    4. //错误,不要用点分语法调用方法
    5. NSArray *array = [NSArray arrayWithObject:@"hello"];
    6. array.release;

    Delegate要使用弱引用

    一个类的Delegate对象通常还引用着类本身,这样很容易造成引用循环的问题,所以类的Delegate属性要设置为弱引用。