了解Cocoa和Objective-C一些特性

了解CocoaObjective-C一些特性是本文要介绍的内容,对于Objective-C的一些特性,初学者应该好好的参考一番,文中介绍的夜很详细。不多说,我们来看内容。
 
1、成员变量应该定义为@private

参考代码:

@interface MyClass : NSObject {  


 @private  


  id myInstanceVariable_;  


}  


// public accessors, setter takes ownership  


- (id)myInstanceVariable;  


- (void)setMyInstanceVariable:(id)theVar;  


@end 

2、明确指定初始化

注释并说明指定的初始化。

明确指定初始化对想要子类化你的类的时候时很重要的。那样,子类化时只需要做一个或多个初始化去保证初值即可。这也有助于在以后调试你的类时明了初始化流程。

3、重写指定初始化

当重写一个子类并需要init...方法,注意要重写父类的指定初始化方法。

如果你没有正确重写父类的指定初始化方法,你的初始化方法可能不会被调用,这会导致很多微妙而难以排除的错误。

4、重写NSObject的方法

强烈建议在@implementation之后就立即重写NSObject 的方法。建议重写 init...,copyWithZone:和 dealloc 方法。init...相关的方法写在一起, 接下来是 copyWithZone: ,最后是 dealloc。

5、避免调用new方法

不要调用NSObject 的new方法,也不要在子类中重写它,而是应该使用 alloc 和 init 方法来初始化retained的对象。

Objective-C代码显式调用 alloc 和 init 方法来创建和retain一个对象。new 的方法可能会带来内存上调试的麻烦。

6、初始化变量

没必要在初始化方法里把变量初始化为0或者nil,这是多余的。

所有新分配内存的对象内容都初始化为0(除了isa),所以不要在init方法里做无谓的重初始化为0的操作。

7、保持公有API简明

保持你的类简单,如果一个方法没必要公开就不要公开。使用私有类别保证公开头文件的简洁。

和C++不同,Objective-C无法区分公有私有方法,因为它全是公有的。因此,除非就是为了让用户调用所设计,不要把其他的方法放到公有API里。这样可以减少不期调用的可能性。这还包括重写父类的方法。对于那些内部实现的方法,在实现文件里使用类别而不是将方法定义在公有头文件里。

// GTMFoo.m  


 


#import "GTMFoo.h"   


@interface GTMFoo (PrivateDelegateHandling)  


- (NSString *)doSomethingWithDelegate;  // Declare private method  


@end  


@implementation GTMFoo(PrivateDelegateHandling)  


...  


- (NSString *)doSomethingWithDelegate {  


  // Implement this method  


}  


...  


@end 

8、#import和#include

用#import导入Objective-C或Objective-C++头文件,用#include导入C或C++头文件

根据头文件的语言去选择合适的导入方式。

当导入的头文件使用Objective-C或Objective-C++语言时,使用#import。

当导入标准C或C++头文件时,使用#include。头文件应该使用自己的#define重加载保护。

有些Objective-C头文件没有#define重加载保护,所以只应该用#import导入。因此Objective-C头文件只应该被Objective-C源文件或其他的Objective-C头文件所导入。这种情况下全部使用#import是合适的。

标准C和C++头文件不包含任何Objective-C元素都可以被一般的C或C++文件导入。因为标准C和C++里根本没有#import,所以也只能用#include导入。在Objective-C代码中使用#include一致的导入这些头文件。

本条款有助于跨平台项目的无意错误。一位Mac开发者引入一份新C或C++头文件时可能会忘记添加#define重加载保护,因为在Mac上用#import导入文件不会引发问题,但在别的使用#include的平台就可能出问题。在所有平台一致的使用#include意味着要么全部成功要么全部失败,避免了那种一些平台上可以运作而另一些不行的情况。

#import <Cocoa/Cocoa.h> 



#include <CoreFoundation/CoreFoundation.h> 



#import "GTMFoo.h"  


#include "base/basictypes.h" 

9、使用根框架

导入框架根的头文件而不是分别导入框架头文件

看起来从Cocoa或Foundation这些框架里导入个别的文件很不错,但实际上你直接导入框架根头文件效率更高。框架根已经被预编译故可更快的被加载。还有,记住用#import指令而不是#include导入Objective-C的框架。

#import <Foundation/Foundation.h>     // good  



#import <Foundation/NSArray.h>        // avoid  




#import <Foundation/NSString.h> 

10、构建时即设定autorelease

当创建新的临时对象时,在同一行代码里就设定autorelease而不是写到这个方法的后面几行去

即使这样可能会造成一些轻微的延迟,但这样避免了谁不小心把release去掉,或在release之前就return而造成的内存泄露,如下:

// AVOID (unless you have a compelling performance reason)  



MyController* controller = [[MyController alloc] init];  



// ... code here that might return ...  


[controller release];  


// BETTER  



MyController* controller = [[[MyController alloc] init] autorelease]; 

11、优先autorelease而非retain

对象赋值时尽量采用autorelease而不是retian模式。

当把一个新创建的对象赋予一个变量的时候,第一件要做的事情就是先释放原来变量指向的对象以防止内存泄露。这里也有很多"正确的"方法去做这件事。我们选择autorelease时因为它更不倾向于出错。小心在密集的循环里可能会很快填满autorelease池,而且它也确实会降低效率,但权衡下来还是可以接受的。

- (void)setFoo:(GMFoo *)aFoo {  


  [foo_ autorelease];  // Won't dealloc if |foo_| == |aFoo|  



  foo_ = [aFoo retain];  



} 

12、以声明时的顺序dealloc处理实例变量
dealloc应该用在@interface声明时同样的顺序处理实例变量,这也有助于评审者鉴别。

代码评审者检查或修正dealloc的实现要确保所有retain的实例变量都获得了释放。

为了简化评审dealloc,将释放retain的实例变量代码保持和@interface里声明的顺序一致。如果dealloc调用了其他方法去释放实例变量,添加注释说明那些实例变量被这些方法所处理了。

13、Setters copy NSStrings

在NSString上调用Setters方法时,永远使用copy方式。永远不要retain一个字符串,这可以防止调用者在你不知到的情况下修改了字符串。不要以为你可以改变NSString的值,只有NSMutableString才能做到。

- (void)setFoo:(NSString *)aFoo {  


  [foo_ autorelease];  



  foo_ = [aFoo copy];  



} 

14、避免抛出异常

不要@throwObjective-C的异常,不过你还是要做好准备捕获第三方以及系统调用抛出的异常。

我们的确在编译时加入了-fobjc-exceptions指令(主要是为了获得@synchronized),但我们并不@throw。当然在使用第三方库的时候是允许使用@try,@catch,以及@finally的。如果你确实使用了,请务必明确到文档中哪个方向你想抛出什么异常。

除非你写的代码想要泡在MacOS10.2或更之前,否则不要使用NS_DURING,NS_HANDLER,NS_ENDHANDLER,NS_VALUERETURNandNS_VOIDRETURN这些宏。

另外你要小心当写Objective-C++代码的时候,如果抛出Objective-C异常,那些栈上的对象不会被清理。示例:

class exceptiontest {  


 public:  


  exceptiontest() { NSLog(@"Created"); }  


  ~exceptiontest() { NSLog(@"Destroyed"); }  


};  


void foo() {  


  exceptiontest a;  



  NSException *exception = [NSException exceptionWithName:@"foo"  



                                                   reason:@"bar"  


                                                 userInfo:nil];  


  @throw exception;  


}  


int main(int argc, char *argv[]) {  


  GMAutoreleasePool pool;  


  @try {  


    foo();  


  }  


  @catch(NSException *ex) {  


    NSLog(@"exception raised");  


  }  


  return 0;  


} 

将会有如下输出:

2006-09-28 12:34:29.244 exceptiontest[23661] Created  


2006-09-28 12:34:29.244 exceptiontest[23661] exception raised 

注意这里的析构函数永远没有机会被调用。这是在你想用栈上的智能指针比如shared_ptr,linked_ptr,还有STL对象的时候不得不关注的一个核心问题。如果你一定要在Objective-C++代码里抛出异常,那就请一定使用C++的异常。永远不要重新抛出一个Objective-C的异常,也不允许在异常块即@try,@catch,@finally里生成栈上的C++对象(比如std::string,std::vector等)。

15、nil检查

仅在校验逻辑流程时做nil检查。

使用nil检查不是为了防止程序崩溃,而是校验逻辑流程。向一个空对象发送一条消息是由Objective-C运行时处理的。方法没有返回结果,你也可以安心走下去。

注意这里和C/C++的空指针检查是完全不同的,在那些环境里,并不处理空指针情况并可能导致你的应用程序崩溃。不过你仍要自己确保提领的指针不为空。

16、 BOOL类型陷阱

整形的转换为BOOL型的时候要小心。不要直接和YES做比较。

BOOL在Objective-C里被定义为unsignedchar,这意味着它不仅仅只有YES(1)和NO(0)两个值。不要直接把整形强制转换为BOOL型。常见的错误发生在把数组大小,指针的值或者逻辑位运算的结果赋值到BOOL型中,而这样就导致BOOL值的仅取决于之前整形值的最后一个字节,有可能出现整形值不为0但被转为NO的情况。应此把整形转为BOOL型的时候请使用ternery操作符,保证返回YES或NO值。

在BOOL,_BOOL以及bool(见C++Std4.7.4,4.12以及C99Std6.3.1.2)之间可以安全的交换值或转型。但BOOL和Boolean之间不可,所以对待Boolean就像上面讲的整形一样就可以了。在Objective-C函数签名里仅使用BOOL。

对BOOL值使用逻辑运算(&&,||,!)都是有效的,返回值也可以安全的转为BOOL型而不需要ternery操作符。

- (BOOL)isBold {  


  return [self fontTraits] & NSFontBoldTrait;  


}  


- (BOOL)isValid {  


  return [self stringValue];  


}  


- (BOOL)isBold {  


  return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;  


}  


- (BOOL)isValid {  


  return [self stringValue] != nil;  


}  


 


- (BOOL)isEnabled {  


  return [self isValid] && [self isBold];  


} 

还有,不要把BOOL型变量直接与YES比较。这样不仅对于精通C的人很有难度,而且此条款的第一点也说明了这样做未必能得到你想要的结果。

BOOL great = [foo isGreat];  



if (great == YES)  



  // ...be great!  



BOOL great = [foo isGreat];  



if (great)  


// ...be great! 

17、属性

属性遵循如下规则:属性是Objective-C2.0的特性,所以只能跑在iPhone以及MacOSX10.5(leopard)或更高的版本。

一个有属性关联实例变量都要在后面加下划线,而该属性的名称就是实例变量不加尾部的下划线的名字。

使用@synthesize标识以正确的重命名属性。

@interface MyClass : NSObject {  


 @private  


  NSString *name_;  


}  


@property(copy, nonatomic) NSString *name;  


@end  


@implementation MyClass  



@synthesize name = name_;  



@end 

属性的声明必须紧接变量申明的括号后。属性的定义应该紧接@implementation模块后面。它和@interface 或者@implementation 的缩进是相同的。

@interface MyClass : NSObject {  


 @private  


  NSString *name_;  


}  


@property(copy, nonatomic) NSString *name;  


@end  


@implementation MyClass  



@synthesize name = name_;  



- (id)init {  


...  


}  


@end 

18、为NSString使用Copy属性

NSString的属性定义为copy。

相关推荐