了解Cocoa和Objective-C一些特性
了解Cocoa和Objective-C一些特性是本文要介紹的內(nèi)容,對(duì)于Objective-C的一些特性,初學(xué)者應(yīng)該好好的參考一番,文中介紹的夜很詳細(xì)。不多說(shuō),我們來(lái)看內(nèi)容。
1、成員變量應(yīng)該定義為@private
參考代碼:
- @interface MyClass : NSObject {
- @private
- id myInstanceVariable_;
- }
- // public accessors, setter takes ownership
- - (id)myInstanceVariable;
- - (void)setMyInstanceVariable:(id)theVar;
- @end
2、明確指定初始化
注釋并說(shuō)明指定的初始化。
明確指定初始化對(duì)想要子類化你的類的時(shí)候時(shí)很重要的。那樣,子類化時(shí)只需要做一個(gè)或多個(gè)初始化去保證初值即可。這也有助于在以后調(diào)試你的類時(shí)明了初始化流程。
3、重寫指定初始化
當(dāng)重寫一個(gè)子類并需要init...方法,注意要重寫父類的指定初始化方法。
如果你沒(méi)有正確重寫父類的指定初始化方法,你的初始化方法可能不會(huì)被調(diào)用,這會(huì)導(dǎo)致很多微妙而難以排除的錯(cuò)誤。
4、重寫NSObject的方法
強(qiáng)烈建議在@implementation之后就立即重寫NSObject 的方法。建議重寫 init...,copyWithZone:和 dealloc 方法。init...相關(guān)的方法寫在一起, 接下來(lái)是 copyWithZone: ,最后是 dealloc。
5、避免調(diào)用new方法
不要調(diào)用NSObject 的new方法,也不要在子類中重寫它,而是應(yīng)該使用 alloc 和 init 方法來(lái)初始化retained的對(duì)象。
Objective-C代碼顯式調(diào)用 alloc 和 init 方法來(lái)創(chuàng)建和retain一個(gè)對(duì)象。new 的方法可能會(huì)帶來(lái)內(nèi)存上調(diào)試的麻煩。
6、初始化變量
沒(méi)必要在初始化方法里把變量初始化為0或者nil,這是多余的。
所有新分配內(nèi)存的對(duì)象內(nèi)容都初始化為0(除了isa),所以不要在init方法里做無(wú)謂的重初始化為0的操作。
7、保持公有API簡(jiǎn)明
保持你的類簡(jiǎn)單,如果一個(gè)方法沒(méi)必要公開就不要公開。使用私有類別保證公開頭文件的簡(jiǎn)潔。
和C++不同,Objective-C無(wú)法區(qū)分公有私有方法,因?yàn)樗枪械摹R虼?,除非就是為了讓用戶調(diào)用所設(shè)計(jì),不要把其他的方法放到公有API里。這樣可以減少不期調(diào)用的可能性。這還包括重寫父類的方法。對(duì)于那些內(nèi)部實(shí)現(xiàn)的方法,在實(shí)現(xiàn)文件里使用類別而不是將方法定義在公有頭文件里。
- // 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導(dǎo)入Objective-C或Objective-C++頭文件,用#include導(dǎo)入C或C++頭文件
根據(jù)頭文件的語(yǔ)言去選擇合適的導(dǎo)入方式。
當(dāng)導(dǎo)入的頭文件使用Objective-C或Objective-C++語(yǔ)言時(shí),使用#import。
當(dāng)導(dǎo)入標(biāo)準(zhǔn)C或C++頭文件時(shí),使用#include。頭文件應(yīng)該使用自己的#define重加載保護(hù)。
有些Objective-C頭文件沒(méi)有#define重加載保護(hù),所以只應(yīng)該用#import導(dǎo)入。因此Objective-C頭文件只應(yīng)該被Objective-C源文件或其他的Objective-C頭文件所導(dǎo)入。這種情況下全部使用#import是合適的。
標(biāo)準(zhǔn)C和C++頭文件不包含任何Objective-C元素都可以被一般的C或C++文件導(dǎo)入。因?yàn)闃?biāo)準(zhǔn)C和C++里根本沒(méi)有#import,所以也只能用#include導(dǎo)入。在Objective-C代碼中使用#include一致的導(dǎo)入這些頭文件。
本條款有助于跨平臺(tái)項(xiàng)目的無(wú)意錯(cuò)誤。一位Mac開發(fā)者引入一份新C或C++頭文件時(shí)可能會(huì)忘記添加#define重加載保護(hù),因?yàn)樵贛ac上用#import導(dǎo)入文件不會(huì)引發(fā)問(wèn)題,但在別的使用#include的平臺(tái)就可能出問(wèn)題。在所有平臺(tái)一致的使用#include意味著要么全部成功要么全部失敗,避免了那種一些平臺(tái)上可以運(yùn)作而另一些不行的情況。
- #import <Cocoa/Cocoa.h>
- #include <CoreFoundation/CoreFoundation.h>
- #import "GTMFoo.h"
- #include "base/basictypes.h"
9、使用根框架
導(dǎo)入框架根的頭文件而不是分別導(dǎo)入框架頭文件
看起來(lái)從Cocoa或Foundation這些框架里導(dǎo)入個(gè)別的文件很不錯(cuò),但實(shí)際上你直接導(dǎo)入框架根頭文件效率更高??蚣芨呀?jīng)被預(yù)編譯故可更快的被加載。還有,記住用#import指令而不是#include導(dǎo)入Objective-C的框架。
- #import <Foundation/Foundation.h> // good
- #import <Foundation/NSArray.h> // avoid
- #import <Foundation/NSString.h>
10、構(gòu)建時(shí)即設(shè)定autorelease
當(dāng)創(chuàng)建新的臨時(shí)對(duì)象時(shí),在同一行代碼里就設(shè)定autorelease而不是寫到這個(gè)方法的后面幾行去
即使這樣可能會(huì)造成一些輕微的延遲,但這樣避免了誰(shuí)不小心把release去掉,或在release之前就return而造成的內(nèi)存泄露,如下:
- // 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、優(yōu)先autorelease而非retain
對(duì)象賦值時(shí)盡量采用autorelease而不是retian模式。
當(dāng)把一個(gè)新創(chuàng)建的對(duì)象賦予一個(gè)變量的時(shí)候,第一件要做的事情就是先釋放原來(lái)變量指向的對(duì)象以防止內(nèi)存泄露。這里也有很多"正確的"方法去做這件事。我們選擇autorelease時(shí)因?yàn)樗粌A向于出錯(cuò)。小心在密集的循環(huán)里可能會(huì)很快填滿autorelease池,而且它也確實(shí)會(huì)降低效率,但權(quán)衡下來(lái)還是可以接受的。
- - (void)setFoo:(GMFoo *)aFoo {
- [foo_ autorelease]; // Won't dealloc if |foo_| == |aFoo|
- foo_ = [aFoo retain];
- }
12、以聲明時(shí)的順序dealloc處理實(shí)例變量
dealloc應(yīng)該用在@interface聲明時(shí)同樣的順序處理實(shí)例變量,這也有助于評(píng)審者鑒別。
代碼評(píng)審者檢查或修正dealloc的實(shí)現(xiàn)要確保所有retain的實(shí)例變量都獲得了釋放。
為了簡(jiǎn)化評(píng)審dealloc,將釋放retain的實(shí)例變量代碼保持和@interface里聲明的順序一致。如果dealloc調(diào)用了其他方法去釋放實(shí)例變量,添加注釋說(shuō)明那些實(shí)例變量被這些方法所處理了。
13、Setters copy NSStrings
在NSString上調(diào)用Setters方法時(shí),永遠(yuǎn)使用copy方式。永遠(yuǎn)不要retain一個(gè)字符串,這可以防止調(diào)用者在你不知到的情況下修改了字符串。不要以為你可以改變NSString的值,只有NSMutableString才能做到。
- - (void)setFoo:(NSString *)aFoo {
- [foo_ autorelease];
- foo_ = [aFoo copy];
- }
#p#
14、避免拋出異常
不要@throwObjective-C的異常,不過(guò)你還是要做好準(zhǔn)備捕獲第三方以及系統(tǒng)調(diào)用拋出的異常。
我們的確在編譯時(shí)加入了-fobjc-exceptions指令(主要是為了獲得@synchronized),但我們并不@throw。當(dāng)然在使用第三方庫(kù)的時(shí)候是允許使用@try,@catch,以及@finally的。如果你確實(shí)使用了,請(qǐng)務(wù)必明確到文檔中哪個(gè)方向你想拋出什么異常。
除非你寫的代碼想要泡在MacOS10.2或更之前,否則不要使用NS_DURING,NS_HANDLER,NS_ENDHANDLER,NS_VALUERETURNandNS_VOIDRETURN這些宏。
另外你要小心當(dāng)寫Objective-C++代碼的時(shí)候,如果拋出Objective-C異常,那些棧上的對(duì)象不會(huì)被清理。示例:
- 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;
- }
將會(huì)有如下輸出:
- 2006-09-28 12:34:29.244 exceptiontest[23661] Created
- 2006-09-28 12:34:29.244 exceptiontest[23661] exception raised
注意這里的析構(gòu)函數(shù)永遠(yuǎn)沒(méi)有機(jī)會(huì)被調(diào)用。這是在你想用棧上的智能指針比如shared_ptr,linked_ptr,還有STL對(duì)象的時(shí)候不得不關(guān)注的一個(gè)核心問(wèn)題。如果你一定要在Objective-C++代碼里拋出異常,那就請(qǐng)一定使用C++的異常。永遠(yuǎn)不要重新拋出一個(gè)Objective-C的異常,也不允許在異常塊即@try,@catch,@finally里生成棧上的C++對(duì)象(比如std::string,std::vector等)。
15、nil檢查
僅在校驗(yàn)邏輯流程時(shí)做nil檢查。
使用nil檢查不是為了防止程序崩潰,而是校驗(yàn)邏輯流程。向一個(gè)空對(duì)象發(fā)送一條消息是由Objective-C運(yùn)行時(shí)處理的。方法沒(méi)有返回結(jié)果,你也可以安心走下去。
注意這里和C/C++的空指針檢查是完全不同的,在那些環(huán)境里,并不處理空指針情況并可能導(dǎo)致你的應(yīng)用程序崩潰。不過(guò)你仍要自己確保提領(lǐng)的指針不為空。
16、 BOOL類型陷阱
整形的轉(zhuǎn)換為BOOL型的時(shí)候要小心。不要直接和YES做比較。
BOOL在Objective-C里被定義為unsignedchar,這意味著它不僅僅只有YES(1)和NO(0)兩個(gè)值。不要直接把整形強(qiáng)制轉(zhuǎn)換為BOOL型。常見(jiàn)的錯(cuò)誤發(fā)生在把數(shù)組大小,指針的值或者邏輯位運(yùn)算的結(jié)果賦值到BOOL型中,而這樣就導(dǎo)致BOOL值的僅取決于之前整形值的最后一個(gè)字節(jié),有可能出現(xiàn)整形值不為0但被轉(zhuǎn)為NO的情況。應(yīng)此把整形轉(zhuǎn)為BOOL型的時(shí)候請(qǐng)使用ternery操作符,保證返回YES或NO值。
在BOOL,_BOOL以及bool(見(jiàn)C++Std4.7.4,4.12以及C99Std6.3.1.2)之間可以安全的交換值或轉(zhuǎn)型。但BOOL和Boolean之間不可,所以對(duì)待Boolean就像上面講的整形一樣就可以了。在Objective-C函數(shù)簽名里僅使用BOOL。
對(duì)BOOL值使用邏輯運(yùn)算(&&,||,!)都是有效的,返回值也可以安全的轉(zhuǎn)為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比較。這樣不僅對(duì)于精通C的人很有難度,而且此條款的第一點(diǎn)也說(shuō)明了這樣做未必能得到你想要的結(jié)果。
- BOOL great = [foo isGreat];
- if (great == YES)
- // ...be great!
- BOOL great = [foo isGreat];
- if (great)
- // ...be great!
17、屬性
屬性遵循如下規(guī)則:屬性是Objective-C2.0的特性,所以只能跑在iPhone以及MacOSX10.5(leopard)或更高的版本。
一個(gè)有屬性關(guān)聯(lián)實(shí)例變量都要在后面加下劃線,而該屬性的名稱就是實(shí)例變量不加尾部的下劃線的名字。
使用@synthesize標(biāo)識(shí)以正確的重命名屬性。
- @interface MyClass : NSObject {
- @private
- NSString *name_;
- }
- @property(copy, nonatomic) NSString *name;
- @end
- @implementation MyClass
- @synthesize name = name_;
- @end
屬性的聲明必須緊接變量申明的括號(hào)后。屬性的定義應(yīng)該緊接@implementation模塊后面。它和@interface 或者@implementation 的縮進(jìn)是相同的。
- @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。
如果自己實(shí)現(xiàn)setters方法,也請(qǐng)使用copy而不是retain。
小結(jié):了解Cocoa和Objective-C一些特性的內(nèi)容介紹完了,希望本文對(duì)你有所幫助!